C#泛型逆变和协变的高级用法

问题描述 投票:0回答:1

在 Kotlin 中,您可以使用泛型来实现:

class Foo<T> {
    fun toto(arg: Bar<in T>) {}

    fun tata(arg: Baz<out T>) {}
}

C# 中的等价物是什么?

我尝试像这样在 C# 中复制代码

class Foo<T> {
    public void toto(Bar<in T> arg) {}

    public void tata(Baz<out T> arg) {}
}

我预计它会起作用,但它说

Type argument is missing

c# generics covariance
1个回答
0
投票

C# 没有使用站点差异 - 它只有声明站点差异(Kotlin 也有),但仅限于接口类型。也就是说,您只能在接口的声明中指定类型参数的方差,例如

// this just so happens to also be valid Kotlin :)
interface IBar<in T> { ... }
// or
interface IBar<out T> { ... }

“模拟”使用站点差异的一种方法是将类划分为“可以协变使用的东西”(输出位置中的

T
)和“可以逆变使用的东西”(在输出位置中
T
)输入位置)。为这两组中的每组声明一个接口,并具有正确的方差。

interface ICovariantBar<out T> {
    T Output();
}

interface IContravariantBar<in T> {
    void Input(T t);
}

class Bar<T>: ICovariantBar<T>, IContravariantBar<T> {
    public void Input(T t) { ... }
    
    public T Output() { ... }
}

然后您可以在 Kotlin 中使用

ICovariantBar<T>
代替
Bar<out T>
,在 Kotlin 中使用
IContravariantBar<T>
代替
Bar<in T>

这当然有一些注意事项。从我的头顶上看:

  • 随着您添加更多类型参数,您需要的接口数量会组合增长
  • 任何类型都可以实现接口,而不仅仅是
    Bar<T>
  • 在 Kotlin 中,从技术上讲,当
    T
    是协变时,您仍然可以在输入位置使用带有
    T
    的方法,而当
    T
    是逆变时,您仍然可以在输出位置使用带有
    T
    的方法。只是类型
    T
    将分别替换为
    Nothing
    (“底部类型”)或
    T
    的边界。您需要添加一些额外的方法才能在 C# 中执行相同的操作
© www.soinside.com 2019 - 2024. All rights reserved.