什么<in from,=“”out =“”to =“”>是什么意思?</in>

时间:2011-11-29 20:20:27

标签: c# generics covariance contravariance

Resharper建议改变

interface IModelMapper<TFrom, TTo>
{
    TTo Map(TFrom input);
}

进入

interface IModelMapper<in TFrom, out TTo>

所以我调查了一下,结束了阅读this article(通过维基百科文章找到)以及更多的Google。

我仍然不确定这对我的申请意味着什么,所以我很想接受这个建议。这种改变会带来什么好处,我不会忽视这个建议?

更明确地说,我为什么要接受它?

2 个答案:

答案 0 :(得分:48)

底线: Resharper调查了您的类型,发现TFrom可能会被违反使用,而TTo则会同时使用TTo。接受重构将允许您更灵活地使用这些类型,如下所述。如果这对您有价值,请接受它。

但是,请注意,接受此重构会限制您将来如何使用这些类型。如果您编写的方法以TFrom作为参数,则会出现编译错误,因为无法读入协同类型。而out同样如此:您永远无法拥有返回此类型的方法,或者具有此类型的TFrom参数。


这告诉你TTo是逆变的,IEnumerable<T>是协变的。这些是最近的功能added to C#

类型协方差意味着可以传入 more 特定类型,而逆变意味着可以传入 less 特定类型。

IEnumerable<T>是类型协方差的一个很好的例子。由于IEnumerable<object> objects = new List<string>(); 中的项目只读,您可以将其设置为更具体的内容:

List<object> objects = new List<string>();
objects.Add(new Car());
//runtime exception

考虑可能发生的事情如果(假设)你被允许为读/写集合执行此操作:

IEnumerable<T>

要成为类型协变,必须以严格只读方式使用泛型参数;它必须只从类型中写出 out ,而不读中的(因此关键字)。这就是List<T>示例有效的原因,但Action<T>示例却没有。顺便说一下,数组支持类型协方差(因为Java确实如此),因此数组可能会出现同样的运行时错误。

类型逆变意味着相反。要支持类型逆转,通用参数必须仅在 中读取,并且永远不会写入 out 。这允许您替换不太具体的类型。

Action<object> objAction = (o => Console.WriteLine(o.ToString())); Action<string> strAction = objAction; strAction("Hello"); 是类型逆转的一个例子:

strAction
声明

Func<T>采用字符串参数,但如果替换对象类型,它可以正常工作。一个字符串将被传入,但如果它设置为使用的委托选择将其视为一个对象,那么就这样吧。没有伤害。

为了完整性,Action<T>T的相反情况;这里只返回Func<string> strDelegate = () => "Hello"; Func<object> myObjFunc = strDelegate; object O = myObjFunc(); ,因此它是协变的:

myObjectFunc

{{1}}被编码为返回一个对象。如果你把它设置为返回一个字符串的东西,那么,再一次,没有伤害。

答案 1 :(得分:2)

作为此选择可能会如何影响您的应用程序的示例,假设您有一个实现CustomerAddressMapper的类型IModelMapper<Customer, Address[]>和另一个实现SupplierAddressMapper的{​​{1}}。 IModelMapper<Supplier, Address[]>Customer类型共享基类Supplier,但它们的地址逻辑是不同的,因此我们需要单独的类型来处理它。

现在假设你有一个方法需要Company。在界面逆转之前,您将无法将IMapper<Company, Address[]>CustomerAddressMapper的实例传递给此方法。现在,如果您在SupplierAddressMapper类型参数上使用in修饰符,则可以。

此外,由于TFrom参数的协方差,您还可以将TTo传递给需要CustomerAddressMapper的方法。