C# ListInterface:为什么你不能执行 `ListIFoo foo = new ListBar();`

c++

1个回答

写回答

羊羊哥哥

2025-06-24 11:25

+ 关注

AI
AI

为什么你不能执行 List foo = new List();

在C#中,我们经常使用泛型集合来存储和操作对象。泛型集合允许我们在编写代码时指定集合中元素的类型,并在运行时保持类型安全。然而,在某些情况下,我们可能会遇到一些类型转换的问题。

在C#中,接口(Interface)是一种定义了一组成员(方法、属性等)的类型,而类(Class)则是实现这些成员的具体实现。如果一个类实现了某个接口,我们可以将这个类的实例分配给该接口的变量。这种特性允许我们以统一的方式对不同的类进行操作。

考虑以下的代码示例:

csharp

interface IFoo

{

void Bar();

}

class Bar : IFoo

{

public void Bar()

{

Console.WriteLine("Bar");

}

}

class Program

{

static void MAIn(string[] args)

{

List<IFoo> foo = new List<Bar>();

foo.Add(new Bar());

}

}

在上面的代码中,我们定义了一个接口 IFoo,其中包含一个方法 Bar()。然后我们创建了一个类 Bar,它实现了接口 IFoo 中的方法。接下来,我们在 MAIn 方法中创建了一个 List 类型的变量 foo,并将其初始化为一个 List 类型的实例。最后,我们将一个 Bar 类型的实例添加到了 foo 列表中。

尽管我们可以将一个 Bar 类型的实例赋值给 IFoo 类型的变量,但是我们不能将一个 List 类型的实例赋值给 List 类型的变量。这是因为 C# 中的泛型类型不支持协变(Covariance)。

什么是协变(Covariance)?

协变是指在类型转换中,如果派生类型可以隐式转换为基类型,那么这个类型就是协变的。在C#中,协变支持的最常见的情况就是数组类型的协变。

考虑以下的代码示例:

csharp

Bar[] bars = new Bar[] { new Bar() };

IFoo[] foos = bars;

在上面的代码中,我们创建了一个 Bar 类型的数组 bars,并将其赋值给一个 IFoo 类型的数组 foos。这是因为数组类型是协变的,即我们可以将一个派生类型的数组赋值给基类型的数组。

然而,泛型类型不支持协变。这是因为泛型类型的类型参数是在编译时确定的,而不是在运行时确定的。在上面的代码中,List 和 List 实际上是两种不同的类型,即使 Bar 类型实现了 IFoo 接口。因此,我们不能将一个 List 类型的实例赋值给 List 类型的变量。

如何解决这个问题?

为了解决这个问题,我们可以使用接口的泛型版本来实现协变。在 C# 4.0 中引入的协变特性允许我们在接口的泛型参数前加上 out 关键字来声明协变。

考虑以下的代码示例:

csharp

interface IFoo<out T>

{

void Bar();

}

class Bar : IFoo<Bar>

{

public void Bar()

{

Console.WriteLine("Bar");

}

}

class Program

{

static void MAIn(string[] args)

{

List<IFoo<Bar>> foo = new List<Bar>();

foo.Add(new Bar());

}

}

在上面的代码中,我们定义了一个泛型接口 IFoo,其中的 out 关键字表示该接口是协变的。我们还修改了 List 的类型为 List>,这样就可以将一个 List 类型的实例赋值给 List> 类型的变量。这样我们就可以将 Bar 类型的实例添加到 foo 列表中了。

在C#中,我们不能将一个 List 类型的实例赋值给 List 类型的变量。这是因为泛型类型不支持协变。为了解决这个问题,我们可以使用接口的泛型版本来实现协变。通过使用协变特性,我们可以将一个派生类型的实例赋值给基类型的接口泛型参数。这样我们就可以在泛型集合中存储不同类型的对象,并以统一的方式对它们进行操作。

举报有用(4)分享收藏

Copyright © 2025 IZhiDa.com All Rights Reserved.

知答 版权所有 粤ICP备2023042255号