为什么我不能传递此类的实例...
class Item<T> where T : Thing { }
...进入这个方法:
void DoSomething(Item<Thing> item);
由于我被限制Item<T>
为Item<Thing>
,因此在将我的项目发送到之前应该是安全的DoSomething
- 但为什么框架不处理这个?
假设你有一堂课:
public class SomeOtherThing : Thing { }
Item<SomeOtherThing>
不能将An强制转换为Item<Thing>
。他们不一样。
让我们暂时假设该项目看起来像这样:
public class Item<T>
{
public T Value { get; set; }
}
DoSomething
然后可能会做类似的事情:
void DoSomething(Item<Thing> item)
{
item.Value = new Thing();
}
如果您传入一个Item<SomeOtherThing>
to,DoSomething
您现在刚刚分配了一个新Thing
to Value
,但 value 是 type 的属性SomeOtherThing
,您不能Thing
为其设置对象。这会破坏类型系统。编译器知道这是一个选项。因此(以及任何数量的具有相同基本问题的其他操作) aItem<SomeOtherThing>
不能转换为 a Item<Thing>
。
所以,你可以做什么?
好吧,如果您控制 的定义DoSomething
,也许它也应该是通用的。
如果DoSomething
看起来像这样:
void DoSomething<T>(Item<T> item)
where T : Thing
{ }
然后你可以用 a 调用它,Item<SomeOtherThing>
因为以前会导致问题的操作在 内部不再有效DoSomething
。
想象
class Derived : Thing {}
Item<Derived>
不可分配Item<Thing>
给。
您遇到的问题是您的方法特定于Item<Thing>
,并且Item<Something>
不是有效的参数。C# 不支持泛型类协方差。你有几个选择。
“最简单”的事情是简单地使您的方法通用并为 Thing 添加一个约束。这将是第一个建议的方法。
void DoSomething<T>(Item<T> item) where T : Thing
{
}
这将授予您访问所有Thing
适用的成员的权限,同时仍然有一个可以满足所有子类的方法。
另一个有更多限制的选择是有一个非泛型基础,Item<Thing>
即简单的Item
. (在其中,无论您先前暴露 T,您都会暴露object
. 想想IEnumerable<T>
:: IEnumerable
。)
void DoSomething(Item item)
{
}
您的方法只是期望Item
并且您可以使用它。但是,如果您有特定的要求Thing
,它会更微妙,您需要适当地进行转换,但您也面临有人传递一个不是 where T is a Thing 的实例的可能性Item<T>
。
另一种选择是单独保留方法的签名并将类转换为接口,这允许您使用协方差。(.NET 4.0 支持接口和委托类型的协/逆变。)
interface Item<out T>
{
T Get();
// void Set(T foo); // invalid
}
同样,这里的问题是 1)您需要更改类型(显然)但是 2)您将被限制为仅T
在输出位置公开。请注意,该Get()
方法受支持。方法Set(T)
不是,因为T
是输入,这使得接口不是协变有效的。(想象一下传入 an ,而您的Item<Something>
方法尝试Set
使用SomeOtherThing
.Things
T
你需要像这样在方法声明中更具体一点;
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
}