【发布时间】:2010-10-10 08:43:54
【问题描述】:
这有什么区别:
void MyMethod(IMyInterface value)
{
//...
}
还有这个:
void MyMethod<T>(T value) where T : IMyInterface
{
//...
}
【问题讨论】:
这有什么区别:
void MyMethod(IMyInterface value)
{
//...
}
还有这个:
void MyMethod<T>(T value) where T : IMyInterface
{
//...
}
【问题讨论】:
主要的功能区别是您可以知道泛型方法内部对象的实际类型。 T 参数将包含在某些情况下可能有利的实际类型。
在非泛型情况下,您不能保证访问对象的底层类型。您可以获取大多数类型 value.GetType() 但用户可以传递 Null 并阻止您。
【讨论】:
Jared 提到了一些要点;另一个有趣的:使用泛型,你可以避免值类型的装箱只要你基本上不碰它......所以我可以有一个struct Foo : IMyInterface并将它传递进去,然后它不会被装箱。
集合之类的差异变得更加明显:
static void Foo(IEnumerable<IMyInterface> data) {}
对
static void Foo<T>(IEnumerable<T> data)
where T : IMyInterface {}
现在,由于 C# 3.0 没有协方差(数组除外),我无法将 List<Bar> 传递到顶部,即使 Bar : IMyInterface - 但我可以使用第二个(隐式 T = Bar )。
【讨论】:
IEnumerator<T> 更有趣,因为一些更常见的枚举器实际上是结构体。
IEnumerable<T>.GetEnumerator(),则不能使用它;它只会将该版本用于 foreach 而不是事物本身
IEnumerator<T>.GetEnumerator(),那是正确的。但是,如果您说using(var myEn = myList.GetEnumerator(); doSomething(ref myEn); 和doSomething 采用IEnumerator<T>-约束泛型,它可以将事物作为结构接收(请注意,如果不小心总是通过ref 传递它,语义将与裸接口类型不同;此外,泛型类型的分配可能会获取枚举状态的快照(适用于某些结构类型的枚举器,但不能适用于任何类类型的枚举器)。
通用版本需要 .NET 2.0。
但是说真的,虽然它们看起来很相似,但它们之间存在根本差异。一个区别是,在运行时,JIT 编译器将为将用于通用版本的每种值类型生成代码。非泛型版本需要将值类型装箱才能传递给函数。
在处理代表时,差异也很重要。 MyMethod<int> 的签名匹配 void MyDelegate(int x) 而非泛型版本不匹配。
【讨论】:
另一个区别是,使用泛型方法允许您指定对象必须实现的多个接口:
class Dictionary<TKey,TVal>
where TKey: IComparable, IEnumerable
where TVal: IValue
{ ... }
【讨论】:
另一个细微的区别是您不能仅在约束上重载方法(约束不是方法签名的一部分):
这是非法的:
void MyMethod<T>(T value) where T : IMyInterface
{
//...
}
void MyMethod<T>(T value) where T : IMyInterface2
{
//...
}
虽然这是合法的:
void MyMethod(IMyInterface value)
{
//...
}
void MyMethod(IMyInterface2 value)
{
//...
}
【讨论】:
在这种情况下要考虑的另一个警告是,在泛型中使用“where T : ”可能会过度使用,从而使您的泛型类型本质上是非泛型的。
IE:请记住,通过将泛型方法隔离到 IMyInterface,您将该方法隔离到仅实现 IMyInterface 的那些类型。因此,如果您只是根据良好的 OOP 原则选择使用 IMyInterface,但在任何地方都只有一个(或在某些情况下是极少数)潜在类型来实现该接口,那么您就违背了目的使用泛型。在这种情况下,第一种选择会更好。
仅当您将拥有更多实际实现 IMyInterface 的类型时,才在泛型类型上使用“where”。
【讨论】:
一般来说,泛型方法的另一个区别(尽管不是你的例子)是,如果一个方法像T MungeThing<T>(T it) where T:IMungeable<T> 和类Fnord 实现IMungeable<Fnord>,那么代码将能够说:Fnord thing1, thing2; ... thing1 = MungeThing(thing2);并且编译器会知道MungeThing 将返回Fnord 而不是IMungable 的任意实现。
【讨论】: