6

我有一个基类,它定义了一个像这样的泛型方法:

public class BaseClass
{
    public T DoSomething<T> ()
    { ... }
}

由于这个类是由第三方提供的并且没有接口,我正在定义一个接口,该接口定义了该类中实际需要的方法。这样我就得到了松耦合,实际上可以用其他东西交换那个第三方类。对于此示例,请考虑以下接口:

public interface ISomething
{
    T DoSomething<T> ()
        where T : Foo;
}

如您所见,它定义了相同的方法,但也对类型参数应用了类型约束,这来自与此无关的一些其他要求。

接下来,我定义了一个子类型,BaseClass它也实现了ISomething. 此类将用作接口背后的常用实现——而接口将是应用程序的其余部分将访问的内容。

public class Something : BaseClass, ISomething
{
    // ...
}

由于DoSomethinginBaseClass已经支持任何类型参数T,它应该特别支持类型参数,它是 的子类型Foo。所以人们会期望一个子类型BaseClass已经实现了接口。但是我收到以下错误:

方法“BaseClass.DoSomething()”的类型参数“T”的约束必须匹配接口方法“ISomething.DoSomething()”的类型参数“T”的约束。考虑改用显式接口实现。

现在,我有两种可能;第一个是做错误提示的事情并明确地实现接口。第二个是使用隐藏基本实现new

// Explicit implementation
T ISomething.DoSomething<T> ()
{
    return base.DoSomething<T>();
}

// Method hiding
public new T DoSomething<T>()
    where T : Foo
{
    return base.DoSomething<T>();
}

两者都有效,尽管我可能更喜欢第二种解决方案来保持该方法可以从类本身访问。但是它仍然留下以下问题:

当基类型已经使用不太严格(读取:无)类型约束实现它时,为什么我必须重新实现该方法?为什么该方法需要完全按原样执行?

编辑:为了赋予该方法更多意义,我将返回类型从 更改voidT。在我的实际应用程序中,我有通用参数和返回值。

4

3 回答 3

3

尝试使用组合而不是继承来实现Something

public class Something : ISomething
{
    private readonly BaseClass inner = ...;

    void DoSomething<T>() where T : Foo
    {
        inner.DoSomething<T>();
    }
}
于 2013-01-30T15:29:03.220 回答
1

当然,给定的代码可以安全地编译和运行:

当一个Something实例被键入为SomethingBaseClass编译器将允许任何类型时T,而当相同的实例被键入时,ISomething它只允许类型继承Foo。在这两种情况下,您都可以像往常一样获得静态检查和运行时安全性。

事实上,上述场景正是ISomething显式实现时发生的情况。因此,让我们看看我们可以提出哪些论据来支持和反对当前的事态。

为了:

  • 提议的解决方案并非适用于所有情况;它取决于确切的方法签名(类型参数是协变的吗?逆变的?不变的?)
  • 它不需要用说明如何处理此类情况的新文本来修改规范
  • 它使代码自我记录——你不必学习所说的文本;当前关于显式接口实现的规则就足够了
  • 它不会增加 C# 编译器团队的开发成本(文档、功能实现、测试等)

反对:

  • 你需要输入更多

考虑到上述情况以及这不是日常情况的事实,恕我直言,要得出的结论很明确:这可能很好,但它当然不保证您会不遗余力地实施它。

于 2013-01-30T15:41:55.760 回答
1

你可以用下面的代码得到你想要的。通过在接口定义中包含类型参数,您可以使其看起来满足编译器的协变。该类Base保持不变,您可以隐藏Base实现并使用单个方法实现接口。

class Program
{
    static void Main()
    {
        var something = new Something<Foo>();
        var baseClass = (BaseClass)something;
        var isomething = (ISomething<Foo>)something;

        var baseResult = baseClass.DoSomething<Bar>();
        var interfaceResult = isomething.DoSomething<Bar>();
        var result = something.DoSomething<Bar>();
    }
}

class Foo 
{
}

class Bar : Foo
{
}

class BaseClass
{
    public T DoSomething<T>()
    {
        return default(T);
    }
}

interface ISomething<out T> where T : Foo
{
    T DoSomething<T>();
}

class Something<T> : BaseClass, ISomething<T> where T : Foo
{
    public new T DoSomething<T>()
    {
        return default(T);
    }
}

或者如果你真的不想Foo在实例化中指定

class Program
{
    static void Main()
    {
        var something = new Something();
        var baseClass = (BaseClass)something;
        var isomething = (ISomething)something;

        var baseResult = baseClass.DoSomething<Bar>();
        var interfaceResult = isomething.DoSomething<Bar>();
        var result = something.DoSomething<Bar>();
    }
}

class Foo 
{
}

class Bar : Foo
{
}

class BaseClass
{
    public T DoSomething<T>()
    {
        return default(T);
    }
}

interface ISomething
{
    T DoSomething<T>;
}

interface ISomething<S> : ISomething where S : Foo
{
    new R DoSomething<R>() where R : Foo;
}

class Something : BaseClass, ISomething
{
    public new T DoSomething<T>()
    {
        return default(T);
    }
}
于 2013-01-30T15:43:35.457 回答