我知道IComparable
和IComparable<T>
之间存在很大差异,请参阅this,但在此搜索方法中它不会有任何区别,或者它会不会? / p>
public static int Search<T>(List<T> a, T target) where T : IComparable
{
for (int i = 0; i < a.Count; i++)
{
if (target.CompareTo(a[i]) == 0)
return i;
}
return -1;
}
与之相比:
public static int Search<T>(List<T> a, T target) where T : IComparable<T>
{
...
}
两者都能奏效并给出相同的结果吗?
答案 0 :(得分:8)
两者都能奏效并给出相同的结果吗?
让我们看看编译器为这两种搜索方法发出的内容:
Search:
IL_0000: ldc.i4.0
IL_0001: stloc.0 // i
IL_0002: br.s IL_0025
IL_0004: ldarga.s 01
IL_0006: ldarg.0
IL_0007: ldloc.0 // i
IL_0008: callvirt 06 00 00 0A
IL_000D: box 03 00 00 1B <---- Notice this!
IL_0012: constrained. 03 00 00 1B
IL_0018: callvirt System.IComparable.CompareTo
IL_001D: brtrue.s IL_0021
IL_001F: ldloc.0 // i
IL_0020: ret
IL_0021: ldloc.0 // i
IL_0022: ldc.i4.1
IL_0023: add
IL_0024: stloc.0 // i
IL_0025: ldloc.0 // i
IL_0026: ldarg.0
IL_0027: callvirt 08 00 00 0A
IL_002C: blt.s IL_0004
IL_002E: ldc.i4.m1
IL_002F: ret
SearchGeneric:
IL_0000: ldc.i4.0
IL_0001: stloc.0 // i
IL_0002: br.s IL_0020
IL_0004: ldarga.s 01
IL_0006: ldarg.0
IL_0007: ldloc.0 // i
IL_0008: callvirt 06 00 00 0A
IL_000D: constrained. 03 00 00 1B
IL_0013: callvirt 09 00 00 0A
IL_0018: brtrue.s IL_001C
IL_001A: ldloc.0 // i
IL_001B: ret
IL_001C: ldloc.0 // i
IL_001D: ldc.i4.1
IL_001E: add
IL_001F: stloc.0 // i
IL_0020: ldloc.0 // i
IL_0021: ldarg.0
IL_0022: callvirt 08 00 00 0A
IL_0027: blt.s IL_0004
IL_0029: ldc.i4.m1
IL_002A: ret
如果仔细查看,您会发现主要的不同之处在于Search
中的每次通话,在调用CompareTo
之前必须包装值(这在box
操作中很明显),因为非通用版本接受object
类型。
让我们尝试使用value type分析两者之间的效果差异。我将使用BenchmarkDotNet
,这是一个小的(并且真正令人敬畏的)基准测试框架,它负责JITing,CPU预热等。
测试:
[BenchmarkTask(platform: BenchmarkPlatform.X86)]
[BenchmarkTask(platform: BenchmarkPlatform.X64)]
public class Test
{
private readonly List<int> list = Enumerable.Range(0, 1000000).ToList();
[Benchmark]
public void TestSearch()
{
Search(list, 999999);
}
[Benchmark]
public void TestSearchGeneric()
{
SearchGeneric(list, 999999);
}
public static int Search<T>(List<T> a, T target) where T : IComparable
{
for (int i = 0; i < a.Count; i++)
{
if (target.CompareTo(a[i]) == 0)
return i;
}
return -1;
}
public static int SearchGeneric<T>(List<T> a, T target) where T : IComparable<T>
{
for (int i = 0; i < a.Count; i++)
{
if (target.CompareTo(a[i]) == 0)
return i;
}
return -1;
}
}
结果:
***** Competition: Finish *****
BenchmarkDotNet=v0.7.8.0
OS=Microsoft Windows NT 6.1.7601 Service Pack 1
Processor=Intel(R) Core(TM) i7-4600U CPU @ 2.10GHz, ProcessorCount=4
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit
Type=Test Mode=Throughput Jit=HostJit .NET=HostFramework
Method | Platform | AvrTime | StdDev | op/s |
------------------ |--------- |----------- |---------- |------- |
TestSearch | X64 | 35.8065 ms | 3.3439 ms | 27.93 |
TestSearchGeneric | X64 | 4.6427 ms | 0.3075 ms | 215.40 |
TestSearch | X86 | 26.4876 ms | 1.4776 ms | 37.75 |
TestSearchGeneric | X86 | 6.6500 ms | 0.1664 ms | 150.38 |
***** Competition: End *****
请注意,在x86上发生装箱操作的非通用方法超过4倍,在x64上超过8倍。这可能会对您的应用程序性能产生影响。
我通常不使用非泛型IComparable
,这主要是为了向后兼容泛型之前的日子。请注意,另一个不小的重要因素是通过泛型获得的类型安全性。
答案 1 :(得分:3)
这是一个巨大的差异,第一种方法是拳击价值类型无缘无故。
答案 2 :(得分:2)
第一个(i.e. IComparable)
实现的不是泛型类型,与父类的类型无关,但第二个(即IComparable<T>
)是类型安全的,你只能使用类型您为父类指定的。
答案 3 :(得分:2)
最大的区别应该是显而易见的:版本where T : IComparable
可以与实现IComparable
的类型一起使用。版本where T : IComparable<T>
可以与实现IComparable<T>
的类型一起使用。实现IComparable
但不是IComparable<T>
的常用类型是各种System.Tuple<...>
泛型类型,以及所有枚举类型。