我不确定这两个签名是否有任何真正的区别:
public static class MyCustomExtensions
{
public static bool IsFoo(this IComparable<T> value, T other)
where T : IComparable<T>
{
// ...
}
public static bool IsFoo(this T value, T other)
where T : IComparable<T>
{
// ...
}
}
我认为这些操作几乎完全相同,但我不太确定......我在这里俯瞰什么?
答案 0 :(得分:5)
是的。
第一个签名将匹配可与T
进行比较的任何类型,而不仅仅是T
值。因此,第一个签名可以使用任何实现IComparable<int>
的类型,而不仅仅是int
。
示例:
void Main()
{
10.IsFoo(20).Dump();
new Dummy().IsFoo(20).Dump();
IComparable<int> x = 10;
x.IsFoo(20).Dump();
IComparable<int> y = new Dummy();
y.IsFoo(20).Dump();
}
public class Dummy : IComparable<int>
{
public int CompareTo(int other)
{
return 0;
}
}
public static class Extensions
{
public static bool IsFoo<T>(this IComparable<T> value, T other)
where T : IComparable<T>
{
Debug.WriteLine("1");
return false;
}
public static bool IsFoo<T>(this T value, T other)
where T : IComparable<T>
{
Debug.WriteLine("2");
return false;
}
}
将输出:
2
False
1
False
1
False
1
False
我用LINQPad进行了测试。
答案 1 :(得分:0)
如果我们稍微重写一下,使用IList
代替IComparable
,这不是同一个问题吗?
在这种情况下,很明显IsFoo1
与IsFoo2
完全不同
因为IsFoo1
接受基本上IList<IList<T>>
的第一个参数
而IsFoo2
接受仅IList<T>
public static class MyCustomExtensions
{
public static bool IsFoo1(IList<T> value, T other)
where T : IList<T>
{
// ...
}
public static bool IsFoo2(T value, T other)
where T : IList<T>
{
// ...
}
}
所以不,他们根本不相同。
答案 2 :(得分:0)
他们并不完全相同。在第一个中,您将IComparable<T>
传递给第一个而不是第二个,因此您的实际类型将是<IComparable<IComparable<T>>
和IComparable<T>
。
根据Lee的反馈进行编辑:下面的内容看起来完全相同,但是虽然两者都需要值和其他值来实现IComparable,但第二个也要求它们可以分配给T.
public static bool IsFoo<T>(IComparable<T> value, IComparable<T> other)
{
// ...
}
public static bool IsFoo<T>(T value, T other)
where T : IComparable<T>
{
// ...
}
答案 3 :(得分:0)
差异非常明显。请注意,您必须在方法(泛型方法)或包含类(泛型类,扩展方法无法实现)中定义T
。下面我称之为方法1和2:
public static bool IsFoo1<T>(this IComparable<T> value, T other)
where T : IComparable<T>
{
return true;
}
public static bool IsFoo2<T>(this T value, T other)
where T : IComparable<T>
{
return true;
}
根据T
是值类型还是引用类型,存在差异。您可以使用约束where T : struct, IComparable<T>
或where T : class, IComparable<T>
来限制。
通常使用任何类型T
:
某些疯狂类型X
可能会被声明为IComparable<Y>
,其中Y
与X
不同(且无关)。
使用值类型:
使用IFoo1
时,第一个参数value
将被加框,而value
中的IFoo2
将不会被加框。值类型是密封的,并且逆变量不适用于值类型,因此这是这种情况下最重要的区别。
使用参考类型:
对于引用类型T
,装箱不是问题。但请注意IComparable<>
在其类型参数中是逆变(“in
”)。如果某些非密封类实现IComparable<>
,这很重要。我使用了这两个类:
class C : IComparable<C>
{
public int CompareTo(C other)
{
return 0;
}
}
class D : C
{
}
通过它们,可以进行以下调用,其中一些是因为继承和/或逆转:
// IsFoo1
new C().IsFoo1<C>(new C());
new C().IsFoo1<C>(new D());
new D().IsFoo1<C>(new C());
new D().IsFoo1<C>(new D());
new C().IsFoo1<D>(new D());
new D().IsFoo1<D>(new D());
// IsFoo2
new C().IsFoo2<C>(new C());
new C().IsFoo2<C>(new D());
new D().IsFoo2<C>(new C());
new D().IsFoo2<C>(new D());
//new C().IsFoo2<D>(new D()); // ILLEGAL
new D().IsFoo2<D>(new D());
当然,在许多情况下,通用参数<C>
可以省略,因为它会被推断出来,但为了清楚起见,我将其包括在内。