(更新 - 来自评论)问题:使用一种扩展方法优于另一种方法有什么优势吗?
根据我在codeproject article关于扩展方法的讨论,我不确定以下内容是否正确。
目前,我有以下扩展方法:
public static bool In<T>(this T source, params T[] list)
{
if (null == source) throw new ArgumentNullException("source");
return list.Contains(source);
}
哪个按预期工作。在评论中已经建议我改变它,以便它只检查引用类型,如:
public static bool In<T>(this T source, params T[] list)
{
if (!typeof(T).IsValueType)
{
if (Equals(source, default(T))) throw new ArgumentNullException("source");
}
return list.Contains(source);
}
再次按预期工作。 第二种方法优于第一种方法,鉴于运行快速基准测试,我们谈论的是10000次运行的0.001秒差异。
基准测试输出(Core i3 @ 4ghz,RAID 0 ssd):
Testing performance...
Value type, original: 00:00:00.0033289
Value type, from code project: 00:00:00.0033027
Reference type, original: 00:00:00.0076951
Reference type, from code project: 00:00:00.0068459
基准代码:
Console.WriteLine("Testing performance...");
Console.WriteLine("");
const Int32 _runs = 10000;
Stopwatch sw = new Stopwatch();
Console.Write("Value type, original: ");
sw.Start();
for (Int32 i = 0; i < _runs; i++)
{
try
{
i.In(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
catch (Exception)
{
// do nothing
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString());
sw = new Stopwatch();
Console.Write("Value type, from code project: ");
sw.Start();
for (Int32 i = 0; i < _runs; i++)
{
try
{
i.In2(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
catch (Exception)
{
// do nothing
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString());
sw = new Stopwatch();
Console.Write("Reference type, original: ");
sw.Start();
for (Int32 i = 0; i < _runs; i++)
{
try
{
"This String".In("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
}
catch (Exception)
{
// do nothing
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString());
sw = new Stopwatch();
Console.Write("Reference type, from code project: ");
sw.Start();
for (Int32 i = 0; i < _runs; i++)
{
try
{
"This String".In("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
}
catch (Exception)
{
// do nothing
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString());
Console.WriteLine("");
Console.ReadLine();
public static bool In<T>(this T source, params T[] list)
{
if (source == null) throw new ArgumentNullException("source");
return list.Contains(source);
}
public static bool In2<T>(this T source, params T[] list)
{
if (!typeof(T).IsValueType)
{
if (Equals(source, default(T))) throw new ArgumentNullException("source");
}
return list.Contains(source);
}
答案 0 :(得分:2)
我会将您的代码保留为
public static bool In<T>(this T source, params T[] list)
{
if (null == source) throw new ArgumentNullException("source");
return list.Contains(source);
}
因为它更容易阅读。
在相关的说明中:源可以是值类型吗?如果不是,您可以将T约束为T:class
。
答案 1 :(得分:1)
这两种方法基本相同。
在原始版本中,如果T是值类型,则测试始终失败:值类型永远不等于空指针。因为条件总是假的,所以测试被优化掉了。
在第二个版本中,测试是明确的,但结果完全相同。
我认为没有理由偏爱另一个。您的原始版本可能在值类型上略快一些,但我会添加一条注释来解释它的工作原理。
答案 2 :(得分:1)
除了性能改进之外,由于它使代码的可读性降低(但不是过于严重),因此可以解释,我更担心的是你的两个方法没有相同的语义。确实有值类型可以为null:Nullable<TValue>
,更好地称为TValue?
。
以下代码:
int? nullableInt = null;
nullableInt.In(list);
在第一个实现中抛出ArgumentNullException,而不是在第二个实现中抛出(提供的列表以前已经正确初始化)。