将约束泛型类型传递给非泛型方法

时间:2013-02-18 18:43:36

标签: c# generics .net-4.0 casting

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

class Item<T> where T : Thing { }

...进入这个方法:

void DoSomething(Item<Thing> item);

由于我已将Item<T>限制为Item<Thing>,因此在将项目发送到DoSomething之前进行投射应该是安全的 - 但为什么框架不会处理此问题?

4 个答案:

答案 0 :(得分:2)

假设你有一个班级:

public class SomeOtherThing : Thing { }

Item<SomeOtherThing> <\ n> <\ n> 它们不一样。

让我们暂时假设该项看起来像这样:

Item<Thing>

public class Item<T> { public T Value { get; set; } } 可能会执行以下操作:

DoSomething

如果您将void DoSomething(Item<Thing> item) { item.Value = new Thing(); } 传递给Item<SomeOtherThing>,那么您现在刚刚将DoSomething分配给Thing,但值是{{1}类型的属性1}},您无法设置Value对象。这将破坏类型系统。编译器知道这是一个选项。由于这一点(以及任何其他具有相同基本问题的操作),SomeOtherThing无法转换为Thing

那么,你能做什么?

好吧,如果你控制Item<SomeOtherThing>的定义,也许它也应该是通用的。

如果Item<Thing>看起来像这样:

DoSomething

然后您可以使用DoSomething来调用它,因为之前会导致问题的操作不再在void DoSomething<T>(Item<T> item) where T : Thing { } 内有效。

答案 1 :(得分:1)

想象

class Derived : Thing {}

Item<Derived>无法分配给Item<Thing>

答案 2 :(得分: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
}

答案 3 :(得分: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一起使用,它会更加精致,你需要适当地进行投射,但是你也可能面临某人传递不是<的实例的可能性/ em>一个Item<T>,其中T是一件事。

另一个选项是单独保留方法的签名并从类转换为接口,这允许您使用协方差。 (.NET 4.0中支持接口和委托类型的Co / contravariance。)

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

同样,这里的问题是1)你需要改变你的类型(显然),但是2)你只能在输出位置暴露T。请注意,支持Get()方法。 Set(T)方法不是,因为T是一个输入,这使得接口不具有协同有效性。 (想象一下,传递Item<Something>,并且您的方法尝试使用Set拨打SomeOtherThing。两者都是Things,但显然第二个不适合第一个。)所以如果您需要支持T输出输入,则无法使用此方法。