接口中的类型约束适用于基类

时间:2013-01-30 15:16:29

标签: c# generics type-constraints

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

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

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

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

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

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

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

由于DoSomething中的BaseClass已经支持任何类型参数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>();
}

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

为什么我必须在基类型使用less-strict(read:none)类型约束实现它时重新实现该方法?为什么该方法需要完全按原样实现?

编辑:为了让方法更具意义,我将返回类型从void更改为T。在我的实际应用程序中,我有泛型参数和返回值。

3 个答案:

答案 0 :(得分:3)

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

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

    void DoSomething<T>() where T : Foo
    {
        inner.DoSomething<T>();
    }
}

答案 1 :(得分:1)

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

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

事实上,上面的场景完全当你明确地实现ISomething时会发生什么。那么让我们看看我们可以为当前做什么和反对当前做什么现状。

有关:

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

反对:

  • 您需要输入更多

考虑到上述以及此事实并非日常情况,恕我直言得出的结论很清楚:这个可能很好,但它肯定不值得外出你的实施方式。

答案 2 :(得分:1)

您可以使用下面的代码获得所需内容。通过在接口defenition中包含type参数,您可以使其协变,这似乎满足编译器。 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);
    }
}