1

所以我有一些 Java 代码,它们广泛使用编译得很好的泛型。我将它移植到 C#,如下所示:

interface IFoo1 { }
interface IFoo2 { }

interface IBar<T, K>
    where T : IFoo1
    where K : IFoo2 {
    List<T> GetFoo1s();
    void AddAFoo1(T foo1);

    List<K> GetFoo2s();
    void AddAFoo2(K foo2);
}

interface IBlip<T>
    where T : IBar<IFoo1, IFoo2> {
    T DoBlip(string input);
    void DoBlip2(T input);
}

interface IConverter<T, K>
    where T : IBar<IFoo1, IFoo2>
    where K : IBar<IFoo1, IFoo2> {
    K Convert(T input);
}

class FooA1 : IFoo1 { }
class FooB1 : IFoo1 { }

class FooA2 : IFoo2 { }
class FooB2 : IFoo2 { }

class BarA : IBar<FooA1, FooA2> {
    public List<FooA1> GetFoo1s() { return null;  }
    public void AddAFoo1(FooA1 foo1) { }
    public List<FooA2> GetFoo2s() { return null; }
    public void AddAFoo2(FooA2 foo2) { }
}

class BarB : IBar<FooB1, FooB2> {
    public List<FooB1> GetFoo1s() { return null; }
    public void AddAFoo1(FooB1 foo1) { }
    public List<FooB2> GetFoo2s() { return null; }
    public void AddAFoo2(FooB2 foo2) { }
}

class BlipA : IBlip<BarA> {
    public BarA DoBlip(string input) { return null; }
    public void DoBlip2(BarA input) { }
}

class BlipB : IBlip<BarB> {
    public BarB DoBlip(string input) { return null; }
    public void DoBlip2(BarB input) { }
}

class ConverterImplementation : IConverter<BarA, BarB> {
    public BarB Convert(BarA input) {
        return null;
    }
}

当我编译它时,它抱怨说,例如,使用 ConverterImplementation,BarA 不能隐式转换为 IBar。我想这里有一些我从根本上想念的东西。有人可以解释一下吗?谢谢。

4

2 回答 2

4

默认情况下,泛型类型参数既不是逆变的也不是协变的,但可以通过“in”和“out”关键字使其成为其中之一。

在 IBar<T, K> 的情况下,两个类型参数都用作输入和输出,因此不能将它们设为逆变或协变。如果将其重构为两个接口,一个是 T 仅用于输入,K 仅用于输出,一个是 T 仅用于输出,K 仅用于输入,那么您可以使每个类型参数基于协变或逆变关于它的使用。

于 2013-03-22T21:10:06.380 回答
2

IBar不是只读接口,因此您可能无法在 C# 中实现协变。您需要重构并提取一个只读接口,例如 ReadOnlyBar,并对该接口进行协变。(免责声明 - 不是 C# 专家)

另一方面,Java 的通配符可以将接口变为只读和变接口,所以IBar<? extends Animal>是只读的变的,IBar<? extends Tiger>是它的子类型。这很酷,直到您的代码中到处都是通配符。

于 2013-03-22T21:14:42.337 回答