栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

了解C#中的协变和逆变接口

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

了解C#中的协变和逆变接口

使用

<out T>
,您可以将接口引用视为层次结构中的向上引用。

使用

<in T>
,您可以将接口引用在层次结构中视为向下。

让我尝试用更多的英语术语进行解释。

假设您正在从动物园中检索动物清单,并且打算对其进行处理。所有动物(在您的动物园中)都有一个名称和一个唯一的ID。有些动物是哺乳动物,有些是爬行动物,有些是两栖动物,有些是鱼,等等,但它们都是动物。

因此,在您的动物列表(其中包含不同类型的动物)中,您可以说所有的动物都有一个名字,因此显然可以很容易地获得所有动物的名字。

但是,如果仅列出鱼类,但需要像对待动物一样对待鱼类,那行得通吗?直观上讲,它应该可以工作,但是在C#3.0及更低版本中,这段代码将无法编译:

IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>

这样做的原因是,在检索到动物集合之后,编译器不会“知道”您的意图或 可以
做什么。就其所知,可能有一种方法

IEnumerable<T>
可以将对象放回列表中,这有可能使您将不是鱼类的动物放入应该只包含鱼的集合中。

换句话说,编译器不能保证不允许这样做:

animals.Add(new Mammal("Zebra"));

因此,编译器完全拒绝编译您的代码。这就是协方差。

让我们看一下方差。

由于我们的动物园可以处理所有动物,因此它当然可以处理鱼,所以让我们尝试向我们的动物园添加一些鱼。

在C#3.0和更低版本中,这不会编译:

List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>fishes.Add(new Fish("Guppy"));

在这里,即使该方法仅仅因为所有鱼都是动物而返回,编译器仍 可以 允许这段代码:

List<Animal>
因此,如果我们只是将类型更改为此:

List<Animal> fishes = GetAccessToFishes();fishes.Add(new Fish("Guppy"));

这样就可以了,但是编译器无法确定您不是要这样做:

List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>Fish firstFist = fishes[0];

由于列表实际上是动物列表,因此不允许这样做。

因此,反方差和协方差就是您如何处理对象引用以及如何使用它们。

C#4.0中的

in
and
out
关键字专门将接口标记为一个或另一个。使用
in
,您可以将通用类型(通常为T)放置在 input
-positions中,这意味着方法参数和只写属性。

使用

out
,您可以将泛型放置在 输出 位置,即方法返回值,只读属性和out方法参数。

这将使您可以执行与代码有关的操作:

IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>// since we can only get animals *out* of the collection, every fish is an animal// so this is safe

List<T>
在T上同时具有入向和出向,因此它既不是协变也不是反变,而是一个允许您添加对象的接口,如下所示:

interface IWriteOnlyList<in T>{    void Add(T value);}

将允许您执行以下操作:

IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals(); // still returns     IWriteOnlyList<Animal>fishes.Add(new Fish("Guppy")); <-- this is now safe

这是一些展示概念的视频:

  • 协方差和协方差-VS2010 C#Part 1 of 3
  • 协方差和协方差-VS2010 C#Part 2 of 3
  • 协方差和协方差-VS2010 C#第3部分,共3部分

这是一个例子:

namespace SO2719954{    class base { }    class Descendant : base { }    interface IBibbleOut<out T> { }    interface IBibbleIn<in T> { }    class Program    {        static void Main(string[] args)        { // We can do this since every Descendant is also a base // and there is no chance we can put base objects into // the returned object, since T is "out" // We can not, however, put base objects into b, since all // base objects might not be Descendant. IBibbleOut<base> b = GetOutDescendant(); // We can do this since every Descendant is also a base // and we can now put Descendant objects into base // We can not, however, retrieve Descendant objects out // of d, since all base objects might not be Descendant IBibbleIn<Descendant> d = GetInbase();        }        static IBibbleOut<Descendant> GetOutDescendant()        { return null;        }        static IBibbleIn<base> GetInbase()        { return null;        }    }}

没有这些标记,可以编译以下内容:

public List<Descendant> GetDescendants() ...List<base> bases = GetDescendants();bases.Add(new base()); <-- uh-oh, we try to add a base to a Descendant

或这个:

public List<base> Getbases() ...List<Descendant> descendants = Getbases(); <-- uh-oh, we try to treat all bases   as Descendants


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/415356.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号