使用泛型约束实现接口

16

为什么这个不起作用有些出乎意料?

这是编译器的限制,还是不支持它是有道理的?

public class Class1<T> : IInterface
    where T : Test2
{
    public T Test { get; private set; }
}

public class Test2
{
}

internal interface IInterface
{
    Test2 Test { get; }
}

我遇到的错误是:
'ClassLibrary1.Class1<T>' does not implement interface member 'ClassLibrary1.IInterface.Test'. 
'ClassLibrary1.Class1<T>.Test' cannot implement 'ClassLibrary1.IInterface.Test' because it does not have the matching return type of 'ClassLibrary1.Test2'.

请参见https://dev59.com/2m025IYBdhLWcg3w76lq。 - Rawling
为什么Class1需要是泛型的?如果Test的返回类型是Test2,那么将其返回此类型即可。这样你就可以返回任何派生自Test2的类型了。 - Lou
@Lou,想象一下我们用Interface2代替Class2。这就是我现在面临的问题。 - user3303864
5个回答

13

为了更好的修正,实现显式接口:

public class Class1<T> : IInterface
where T : Test2
{
    public T Test { get; private set; }

    Test2 IInterface.Test
    {
        get { ... }
    }
}

然后你就可以避免编译错误。

或者确实可以使用return Test而不是throw new NotImplementedException() - Rawling
@Rawling:这取决于OP,也许他想以不同的方式实现。 - cuongle
很好。不管怎样,这解释了为什么编译器不直接支持这种协变 - 有一个解决方法,因此编译器团队花时间做更重要的事情 :) - Rawling
Lippert先生提出了一个观点,即每个功能,无论多么小,都有很大的成本。因此,我接受了这个答案,因为我倾向于相信编译器没有其他明显的原因来禁止它。 - buckley

5

由于T可以是从Test2派生的任何类型,因此Class1并没有精确实现IInterface接口。

更一般地说,不可能通过返回协变类型来实现接口:

interface IFoo
{
    object Bar { get; }
}

class Broken : IFoo
{
    string Bar { get; } // you cannot expect to implement IFoo this way!
}

Class1没有完全实现IInterface” - 好吧,class1.Test总是返回一个Test2,对吧?同样地,broken.Bar总是返回一个object - Rawling
接口和类中方法的签名不相等,因此该类未实现接口。 - Kirill Bestemyanov

4
将您的界面更改为以下内容,它将被编译:
```html

更改您的界面到这个,它就可以被编译:

```
public class Class1<T> : IInterface<T>
    where T : Test2
{
    public T Test { get; private set; }
}

public class Test2
{
}

internal interface IInterface<T>
    where T : Test2
{
    T Test { get; }
}

1
您是否可以将您的界面通用化,例如:

public class Class1<T> : IInterface<T>
    where T : Test2
{ 
    public T Test { get; private set; } 
} 

public class Test2 
{ 
} 

internal interface IInterface<T>
{ 
    T Test { get; } 
} 

或者你是在尝试避免在接口上使用泛型(这也有很好的理由!)


是的,我一直在尝试避免这种情况,这样我就可以编写通用代码,在编译器需要类型参数的地方不需要。 - buckley

1

接口表明属性Test是Test2类型。在您的实现Class1中,属性Test是某个继承自Test2但不完全相同的类。 要实现您想要的功能,您需要编写类似以下代码:

public class Class1<T> : IInterface
    where T : Test2
{
    private T _test;
    public Test2 Test { get{return _test} }
}

public class Test2
{ 
}

internal interface IInterface 
{
    Test2 Test { get; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接