我有一个可为空类型的字段,其值不是null
. 理论上,以下哪种方法最快:
-
空条件成员访问
ThisCantBeNull?.SomeMember
-
强制转换为不可为空
((MyType)ThisCantBeNull).SomeMember
-
使用可为空类型的
Value
成员ThisCantBeNull.Value.SomeMember
请注意,这只是一个理论问题,这种微小的差异无关紧要,我只对语言的工作原理和后台发生的事情感兴趣。
只是出于好奇:
public struct MyType
{
public int SomeMember { get; set; }
}
一些非常原始的测试,禁用编译器优化:
MyType? thisCantBeNull = new MyType();
MyType someDefaultValue = new MyType();
var t1 = new Action(() => { var r = (thisCantBeNull ?? someDefaultValue).SomeMember; });
var t2 = new Action(() => { var r = ((MyType)thisCantBeNull).SomeMember; });
var t3 = new Action(() => { var r = thisCantBeNull.Value.SomeMember; });
var t4 = new Action(() => { var r = thisCantBeNull.GetValueOrDefault().SomeMember; });
const int times = 1000 * 1000 * 1000;
var r1 = t1.RunNTimes(times);
// Elapsed Timespan = 00:00:14.45115
var r2 = t2.RunNTimes(times);
// Elapsed Timespan = 00:00:07.9415388
var r3 = t3.RunNTimes(times);
// Elapsed Timespan = 00:00:08.0096765
var r4 = t4.RunNTimes(times);
// Elapsed Timespan = 00:00:07.4732878
相同的测试,启用编译器优化:
var r1 = t1.RunNTimes(times);
// Elapsed Timespan = 00:00:02.9142143
var r2 = t2.RunNTimes(times);
// Elapsed Timespan = 00:00:02.4417182
var r3 = t3.RunNTimes(times);
// Elapsed Timespan = 00:00:02.6278304
var r4 = t4.RunNTimes(times);
// Elapsed Timespan = 00:00:02.1725020
RunNTimes
在哪里:
public static TimeSpan RunNTimes(this Action a, int nTimes = 1)
{
if (nTimes == 0)
throw new ArgumentException("0 times not allowed", nameof(nTimes));
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < nTimes; ++i)
a();
stopwatch.Stop();
return stopwatch.Elapsed;;
}
由于您已经在其他地方检查了 null,只需获取Value
,您的第三个选项。
其他两个选项包括额外的检查。
但是,正如评论中所述,性能改进可能很小。