简体   繁体   English

c# 协变通用参数

[英]c# covariant generic parameter

I'm trying to understand this but I didn't get any appropriate results from searching.我试图理解这一点,但我没有从搜索中得到任何适当的结果。

In c# 4, I can do在 c# 4 中,我可以做到

    public interface IFoo<out T>
    {

    }

How is this different from这与

    public interface IFoo<T>
    {

    }

All I know is the out makes the generic parameter covariant (??).我所知道的是out使泛型参数协变(??)。 Can someone explain the usage of <out T> part with an example?有人可以举例说明<out T>部分的用法吗? And also why is applicable only for interfaces and delegates and not for classes?还有为什么只适用于接口和委托而不适用于类?

Sorry if it's a duplicate and close it as such if it is.对不起,如果它是重复的,如果是,请关闭它。

Can someone explain the usage of the out T part with an example?有人可以举例说明 out T 部分的用法吗?

Sure.当然。 IEnumerable<T> is covariant. IEnumerable<T>是协变的。 That means you can do this:这意味着你可以这样做:

static void FeedAll(IEnumerable<Animal> animals) 
{
    foreach(Animal animal in animals) animal.Feed();
}

...

 IEnumerable<Giraffe> giraffes = GetABunchOfGiraffes();
 FeedAll(giraffes);

"Covariant" means that the assignment compatibility relationship of the type argument is preserved in the generic type . “协变”是指在泛型类型中保留了类型实参的赋值兼容关系 Giraffe is assignment compatible with Animal , and therefore that relationship is preserved in the constructed types: IEnumerable<Giraffe> is assignment compatible with IEnumerable<Animal> . GiraffeAnimal的赋值兼容,因此该关系保留在构造类型中: IEnumerable<Giraffe>IEnumerable<Animal>的赋值兼容。

Why is applicable only for interfaces and delegates and not for classes?为什么只适用于接口和委托而不适用于类?

The problem with classes is that classes tend to have mutable fields.类的问题是类往往具有可变字段。 Let's take an example.让我们举个例子。 Suppose we allowed this:假设我们允许这样做:

class C<out T>
{
    private T t;

OK, now think this question through carefully before you go on. OK,现在这个问题在你go之前仔细想清楚。 Can C<T> have any method outside of the constructor that sets the field t to something other than its default? C<T>可以在构造函数之外有任何方法将字段t设置为默认值以外的值吗?

Because it must be typesafe, C<T> can now have no methods that take a T as an argument;因为它必须是类型安全的,所以C<T>现在不能有将 T 作为参数的方法; T can only be returned. T 只能退货。 So who sets t, and where do they get the value they set it from ?那么谁设置 t,他们从哪里获得设置它的值

Covariant class types really only work if the class is immutable .只有当 class 是不可变的时,协变 class 类型才真正起作用。 And we don't have a good way to make immutable classes in C#.而且我们没有在 C# 中创建不可变类的好方法。

I wish we did, but we have to live with the CLR type system that we were given.我希望我们这样做,但我们必须接受我们提供的 CLR 类型系统。 I hope in the future we can have better support for both immutable classes, and for covariant classes.我希望将来我们可以更好地支持不可变类和协变类。

If this feature interests you, consider reading my long series on how we designed and implemented the feature.如果您对此功能感兴趣,请考虑阅读我关于我们如何设计和实现该功能的长系列。 Start from the bottom:从底部开始:

https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/ https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/

If we're talking about generic variance:如果我们谈论的是通用方差:

Covariance is all about values being returned from an operation back to the caller.协方差是关于从操作返回给调用者的值。

Contravariance It's opposite and it's about values being passed into by the caller:逆变它是相反的,它是关于调用者传递的值:

From what I know if a type parameter is only used for output, you can use out.据我所知,如果类型参数仅用于 output,则可以使用 out。 However if the type is only used for input, you can use in. It's the convenience because the compiler cannot be sure if you can remember which form is called covariance and which is called contravariance.但是,如果该类型仅用于输入,则可以使用 in。这很方便,因为编译器无法确定您是否可以记住哪种形式称为协变,哪种形式称为逆变。 If you don't declare them explicitly once the type has been declared, the relevant types of conversion are available implicitly .如果在声明类型后不显式声明它们,则相关的转换类型是隐式可用的。

There is no variance (either covariance or contravariance) in classes because even if you have a class that only uses the type parameter for input (or only uses it for output), you can't specify the in or out modifiers.类中没有方差(协方差或逆变),因为即使您有一个仅将类型参数用于输入(或仅将其用于输出)的 class,您也无法指定 in 或 out 修饰符。 Only interfaces and delegates can have variant type parameters.只有接口和委托可以有变体类型参数。 Firstly the CLR doesn't allow it.首先,CLR 不允许这样做。 From the conceptual point of view Interfaces represent a way of looking at an object from a particular perspective, whereas classes are more actual implementation types.从概念的角度来看,接口代表了一种从特定角度看待 object 的方式,而类是更实际的实现类型。

It means that if you have this:这意味着如果你有这个:

class Parent { } 
class Child : Parent { }

Then an instance of IFoo<Child> is also an instance of IFoo<Parent> .那么IFoo<Child>的实例也是IFoo<Parent>的一个实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM