在 C# 中最快访问可空类型的成员
Fastest access to member of nullable type in C#
我有一个可为空类型的字段,其值不是 null
。
以下哪种方法最快:
无条件成员访问权限
ThisCantBeNull?.SomeMember
强制转换为不可空
((MyType)ThisCantBeNull).SomeMember
Value
可空类型成员的使用
ThisCantBeNull.Value.SomeMember
请注意,这只是一个理论问题,这些细微差别并不重要,我只对语言的工作方式以及后台发生的事情感兴趣。
由于您已经在其他地方检查过 null,所以只需选择 Value
,您的第三个选项。
其他两个选项包括额外检查。
但是,如评论中所述,性能改进可能很小。
出于好奇:
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
。
以下哪种方法最快:
无条件成员访问权限
ThisCantBeNull?.SomeMember
强制转换为不可空
((MyType)ThisCantBeNull).SomeMember
Value
可空类型成员的使用ThisCantBeNull.Value.SomeMember
请注意,这只是一个理论问题,这些细微差别并不重要,我只对语言的工作方式以及后台发生的事情感兴趣。
由于您已经在其他地方检查过 null,所以只需选择 Value
,您的第三个选项。
其他两个选项包括额外检查。
但是,如评论中所述,性能改进可能很小。
出于好奇:
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;;
}