Resharper建议改变
interface IModelMapper<TFrom, TTo>
{
TTo Map(TFrom input);
}
进入
interface IModelMapper<in TFrom, out TTo>
所以我调查了一下,结束了阅读this article(通过维基百科文章找到)以及更多的Google。
我仍然不确定这对我的申请意味着什么,所以我很想接受这个建议。这种改变会带来什么好处,我不会忽视这个建议?
更明确地说,我为什么要接受它?
答案 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
的方法。