1

为什么我不能传递此类的实例...

class Item<T> where T : Thing { }

...进入这个方法:

void DoSomething(Item<Thing> item);

由于我被限制Item<T>Item<Thing>,因此在将我的项目发送到之前应该是安全的DoSomething- 但为什么框架不处理这个?

4

4 回答 4

2

假设你有一堂课:

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您现在刚刚分配了一个新Thingto 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

于 2013-02-18T19:13:17.400 回答
1

想象

class Derived : Thing {}

Item<Derived>不可分配Item<Thing>给。

于 2013-02-18T18:48:21.763 回答
1

您遇到的问题是您的方法特定于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.ThingsT

于 2013-02-18T19:19:02.893 回答
1

你需要像这样在方法声明中更具体一点;

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
}
于 2013-02-18T18:56:24.287 回答