是什么意思?意思是?

发布于 2024-12-18 21:43:08 字数 420 浏览 5 评论 0原文

Resharper 建议从 更改

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

interface IModelMapper<in TFrom, out TTo>

所以我调查了一下并结束了阅读这篇文章(通过维基百科文章找到)和更多谷歌。

我仍然不确定这对我的申请意味着什么,所以我很想不接受这个建议。如果忽略该建议,我不会考虑此更改会带来哪些好处?

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

Resharper has suggested to change from

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

into

interface IModelMapper<in TFrom, out TTo>

So I investigate a little and ended reading this article (found through a Wikipedia article) and some more Google.

I am still not sure what this would imply for my application so I am tempted of not accepting the suggestion. What would are the benefits this change would introduce and I am not considering by ignoring the suggestion?

More explicitly, why should I accept it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜心小果奶 2024-12-25 21:43:08

底线:Resharper 研究了您的类型,并发现 TFrom 可以用于逆变,而 TTo 可以用于协变。接受重构将使您能够更灵活地使用这些类型,如下所述。如果这对您有价值,请接受。

但请注意,接受此重构将对您将来如何使用这些类型设置限制。如果您编写了一个采用 TTo 作为参数的方法,您将收到编译器错误,因为无法读入协变类型。TFrom 也是如此:您'将永远无法拥有返回此类型的方法,或拥有此类型的 out 参数。


这告诉您 TFrom 是逆变的,而 TTo 是协变的。这些是最近的功能 添加到 C#

类型协变意味着可以传入更具体的类型,而逆变意味着可以传入可以传入不太特定的类型。

IEnumerable 是类型协变的一个很好的例子。由于 IEnumerable 中的项目是只读,因此您可以将其设置为更具体的内容:

IEnumerable<object> objects = new List<string>();

考虑如果(假设)会发生什么您可以对读/写的集合执行此操作:

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

要成为类型协变,必须以严格的只读方式使用泛型参数;它只能从类型中写出,而不能读入(因此是关键字)。这就是 IEnumerable 示例有效,但 List 示例无效的原因。顺便说一句,数组确实支持类型协变(我相信 Java 也支持),因此数组也可能出现同样类型的运行时错误。

类型逆变意味着相反。为了支持类型逆变,泛型参数必须只能读入,而不能写出。这允许您替换不太具体的类型。

Action 是类型矛盾的一个示例:

Action<object> objAction = (o => Console.WriteLine(o.ToString()));
Action<string> strAction = objAction;
strAction("Hello");

strAction 被声明为采用字符串参数,但如果您替换对象类型。将传入一个字符串,但如果设置为使用它的委托选择将其视为对象,那么就这样吧。没有造成任何伤害。

为了完整起见,FuncAction 的逆情况;这里仅返回 T,因此它是协变的:

Func<string> strDelegate =  () => "Hello";
Func<object> myObjFunc = strDelegate;
object O = myObjFunc();

myObjectFunc 被编码为返回一个对象。如果您将其设置为返回字符串的值,那么,同样不会造成任何损害。

Bottom Line: Resharper has investigated your type, and discovered that TFrom may be used contravariantly, and TTo covariantly. Accepting the refactor would allow you to use these types with greater flexibility, as described below. If that might be of value to you, accept it.

Note, however, that accepting this refactor would place restrictions on how you use these types in the future. If you ever write a method that takes TTo as a parameter, you'll get a compiler error, since coviariant types cannot be read in. And ditto for TFrom: you'll never be able to have a method that returns this type, or has an out parameter of this type.


That's telling you that TFrom is contravariant, and that TTo is covariant. These were features recently added to C#

Type covariance means that a more specific type may be passed in, while contravariance means that a less specific type may be passed in.

IEnumerable<T> is a good example of type covariance. Since items in an IEnumerable<T> are read only, you may set it to something more specific:

IEnumerable<object> objects = new List<string>();

Consider what could happen if (hypothetically) you were allowed to do this for collections that were read/write:

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

To be type covariant, a generic parameter must be used in a strictly read-only manner; it must only ever be written out from the type, and never read in (hence the keywords). That's why the IEnumerable<T> example works, but the List<T> example doesn't. By the way, arrays do support type covariance (since Java does, I believe), and so this same kind of runtime error is possible with arrays.

Type contravariance means the opposite. To support type contravariance a generic parameter must be read in only, and never written out. This allows you to substitute less specific types in.

Action<T> is an example of type contravaince:

Action<object> objAction = (o => Console.WriteLine(o.ToString()));
Action<string> strAction = objAction;
strAction("Hello");

strAction is declared to take a string parameter, but it works fine if you substitute an object type. A string will be passed in, but if the delegate it's set to work with chooses to treat it as an object, then so be it. No harm done.

For completeness, Func<T> is the inverse case of Action<T>; here T is only returned, therefore it's covariant:

Func<string> strDelegate =  () => "Hello";
Func<object> myObjFunc = strDelegate;
object O = myObjFunc();

myObjectFunc is coded to return an object. If you set it to something that returns a string, then, again, no harm done.

抹茶夏天i‖ 2024-12-25 21:43:08

作为此选择可能如何影响您的应用程序的示例,假设您有一个实现 IModelMapper 的类型 CustomerAddressMapper 和另一个 SupplierAddressMapper 实现 IModelMapperCustomerSupplier 类型共享一个基类 Company,但它们的地址逻辑不同,因此我们需要单独的类型来处理这个问题。

现在假设您有一个采用 IMapper 的方法。在接口逆变之前,您无法将 CustomerAddressMapperSupplierAddressMapper 的实例传递给此方法。现在,如果您在 TFrom 类型参数上使用 in 修饰符,则可以。

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

As an example of how this choice might affect your application, assume you have a type CustomerAddressMapper that implements IModelMapper<Customer, Address[]> and another one SupplierAddressMapper that implements IModelMapper<Supplier, Address[]>. The Customer and Supplier types share a base class Company, but their address logic is distinct, so we need separate types to handle this.

Now assume that you have a method that takes an IMapper<Company, Address[]>. Before interface contravariance, you would not be able to pass instances of CustomerAddressMapper or SupplierAddressMapper to this method. Now, if you use the in modifier on the TFrom type parameter, you can.

Furthermore, because of covariance on the TTo parameter, you can also pass a CustomerAddressMapper to methods that require an IMapper<Customer, object>.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文