简体   繁体   English

将约束的通用类型传递给非通用方法

[英]Passing Constrained Generic Type to Non-Generic Method

Why can't I pass an instance of this class... 为什么我不能传递此类的实例...

class Item<T> where T : Thing { }

...into this method: ...变成这种方法:

void DoSomething(Item<Thing> item);

Since I've constrained Item<T> to be an Item<Thing> , it should be safe to cast before sending my item through to DoSomething - but why doesn't the framework handle this? 由于我已将Item<T>约束为Item<Thing> ,因此在将我的项目发送到DoSomething之前应该可以安全地进行转换-但是为什么框架不处理此问题呢?

Assuming you have a class: 假设您有一堂课:

public class SomeOtherThing : Thing { }

An Item<SomeOtherThing> can not be cast to an Item<Thing> . 无法Item<SomeOtherThing>转换为Item<Thing> They are not the same. 她们不一样。

Let's assume for a moment that item looks something like this: 让我们暂时假设项目看起来像这样:

public class Item<T>
{
    public T Value { get; set; }
}

DoSomething might then do something like: 然后, DoSomething可能会执行以下操作:

void DoSomething(Item<Thing> item)
{
    item.Value = new Thing();
}

If you pass in an Item<SomeOtherThing> to DoSomething you have now just assigned a new Thing to Value , but value is a property of type SomeOtherThing , you cannot set a Thing object to it . 如果将Item<SomeOtherThing>传递给DoSomething您现在刚刚将一个新的Thing分配给Value但是value是SomeOtherThing类型的属性,则无法为其设置Thing对象 This would have then broken the type system. 这样会破坏类型系统。 The compiler knows that this is an option. 编译器知道这是一个选择。 Because of this (and any number of other operations that have the same fundamental problem) a Item<SomeOtherThing> can not be cast to an Item<Thing> . 因此(以及具有相同基本问题的许多其他操作),不能将Item<SomeOtherThing>转换为Item<Thing>

So, what can you do? 所以,你可以做什么?

Well, if you control the definition of DoSomething , perhaps it too should be generic. 好吧,如果您控制DoSomething的定义,也许它也应该是通用的。

If DoSomething looks like this: 如果DoSomething看起来像这样:

void DoSomething<T>(Item<T> item)
    where T : Thing
{    }

Then you can call it with an Item<SomeOtherThing> since the operations that previously would have caused problems are no longer valid from within DoSomething . 然后,您可以使用Item<SomeOtherThing>进行调用,因为以前可能导致问题的操作在DoSomething不再有效。

Imagine 想像

class Derived : Thing {}

The Item<Derived> is not assignable to Item<Thing> . 无法将Item<Derived>分配给Item<Thing>

you need to be a little more specific in the method declaration like so; 您需要像这样在方法声明中更具体一些;

void DoSomething<T>(Item<T> item)
    where T : Thing
{
    // now your method knows that T must be of time Thing and you can use it
}

The issue you have is that your method is specific to Item<Thing> , and Item<Something> is not a valid argument. 您遇到的问题是您的方法特定于Item<Thing> ,而Item<Something>不是有效的参数。 C# does not support generic class covariance. C#不支持通用类协方差。 You have several options. 您有几种选择。

The "easiest" thing to do is to simply make your method generic and add a constraint to Thing. “最简单”的事情就是简单地使您的方法通用,并为Thing添加约束。 This would be the first advised approach. 这将是第一个建议的方法。

void DoSomething<T>(Item<T> item) where T : Thing
{
}

This will grant you access to all members of Thing as applicable, while still having a method that can cater to all subclasses. 这将授予您访问Thing所有成员(如果适用)的权限,同时仍然拥有可以满足所有子类的方法。

Another option with more limitation is to have a non-generic base to Item<Thing> that is simply Item . 另一个有更多限制的选项是让Item<Thing>的非通用基础就是Item (In it, wherever you were previously exposing T, you would expose object . Think: IEnumerable<T> : IEnumerable .) (在其中,无论您以前暴露T的哪个位置,都将暴露object 。请考虑: IEnumerable<T>IEnumerable 。)

void DoSomething(Item item)
{
}

Your method would simply expect Item and you could work with it. 您的方法只需要Item ,就可以使用它。 But if you have a specific requirement to work with Thing , it is a bit more delicate, you would need to cast as appropriate, but you also face the possibility that someone passes an instance that isn't an Item<T> where T is a Thing. 但是,如果您有使用Thing的特定要求,那么它会更加微妙,您需要进行适当的转换,但同时也面临着有人传递的实例不是 Item<T>的可能性,其中T是一个东西。

One other option is to leave the signature of the method alone and convert from a class to an interface, which allows you to use covariance. 另一种选择是不保留方法的签名,并将其从类转换为接口,从而允许您使用协方差。 (Co/contravariance is supported in .NET 4.0 for interface and delegate types.) (.NET 4.0在接口和委托类型中支持Co / contravariance。)

interface Item<out T> 
{
    T Get();
    // void Set(T foo); // invalid  
}

Again, the issues here are that 1) you would need to change your type (obviously) but 2) you would be limited to only exposing T in output positions. 同样,这里的问题是:1)(显然)您需要更改类型,但是2)您将只能在输出位置暴露T Notice the Get() method is supported. 注意,支持Get()方法。 A Set(T) method is not, because T is an input, which makes the interface not covariantly valid. Set(T)方法不是,因为T是输入,这使得该接口不是协变有效的。 (Imagine passing in an Item<Something> , and your method trying to call Set with SomeOtherThing . Both are Things , but clearly the second is not appropriate for the first.) So if you need to support T as both outputs and inputs, you could not utilize this approach. (想象一下传入一个Item<Something> ,并且您的方法尝试使用SomeOtherThing调用Set 。两者都是Things ,但是显然第二个不适用于第一个。)因此,如果您需要同时支持T作为输出输入,则无法利用这种方法。

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

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