次の SE回答 を参照してください。
書くとき
A = A ?? B;
それは同じです
if( null != A )
A = A;
else
A = B;
という意味ですか
if( null == A ) A = B;
パフォーマンスに関して賢明ですか?
または、同じオブジェクトが??
表記の場合、コンパイラーがコードを最適化すると想定できますか?
_??
_のパフォーマンスはごくわずかですが、副作用が無視できない場合もあります。次のプログラムを検討してください。
_using System;
using System.Diagnostics;
using System.Threading;
namespace TestProject
{
class Program
{
private string str = "xxxxxxxxxxxxxxx";
public string Str
{
get
{
return str;
}
set
{
if (str != value)
{
str = value;
}
// Do some work which take 1 second
Thread.Sleep(1000);
}
}
static void Main(string[] args)
{
var p = new Program();
var iterations = 10;
var sw = new Stopwatch();
for (int i = 0; i < iterations; i++)
{
if (i == 1) sw.Start();
if (p.Str == null)
{
p.Str = "yyyy";
}
}
sw.Stop();
var first = sw.Elapsed;
sw.Reset();
for (int i = 0; i < iterations; i++)
{
if (i == 1) sw.Start();
p.Str = p.Str ?? "yyyy";
}
sw.Stop();
var second = sw.Elapsed;
Console.WriteLine(first);
Console.WriteLine(second);
Console.Write("Ratio: ");
Console.WriteLine(second.TotalMilliseconds / first.TotalMilliseconds);
Console.ReadLine();
}
}
}
_
PCで結果を実行します。
_00:00:00.0000015
00:00:08.9995480
Ratio: 5999698.66666667
_
_??
_を使用した追加の割り当てがあり、割り当てのパフォーマンスが保証されない場合があるためです。これにより、パフォーマンスの問題が発生する可能性があります。
_A = A ?? B;
_ではなくif( null == A ) A = B;
を使用します。
パフォーマンスについては心配しないでください。無視できます。
興味がある場合は、Stopwatch
を使用してパフォーマンスをテストするコードを記述してください。ただし、違いを確認するには数百万回の繰り返しが必要になると思います。
また、物事の実装について決して想定することはできません。それらは将来変更される可能性があり、想定を無効にします。
私の仮定では、パフォーマンスの違いは非常に小さいと思われます。私は個人的に読みやすくするためにnullの合体演算子を使用します。それはニースであり、凝縮されていて、要点を十分に伝えています。遅延ロードチェックのために時々それを行います:
_lazyItem = _lazyItem ?? new LazyItem();
私はこれをC#で試したところ、非常にすばやく、メソッドにエラーが発生する可能性があります。次のコードを使用して、2番目の方法は最初の方法よりも約1.75倍長い時間がかかると判断しました。
@ Lockszmith:以下の編集後、比率は1.115になり、最初の実装が優先されます
同じ時間がかかったとしても、組み込みの言語構造を個人的に使用します。これは、より最適化が組み込まれている可能性のある将来のコンパイラーに対して意図をより明確に表すからです。
@ Lockszmith:コメントからの推奨事項を反映するようにコードを編集しました
var A = new object();
var B = new object();
var iterations = 1000000000;
var sw = new Stopwatch();
for (int i = 0; i < iterations; i++)
{
if( i == 1 ) sw.Start();
if (A == null)
{
A = B;
}
}
sw.Stop();
var first = sw.Elapsed;
sw.Reset();
for (int i = 0; i < iterations; i++)
{
if( i == 1 ) sw.Start();
A = A ?? B;
}
sw.Stop();
var second = sw.Elapsed;
first.Dump();
second.Dump();
(first.TotalMilliseconds / second.TotalMilliseconds).Dump("Ratio");
私のアドバイスは、IL(中間言語)を検査し、さまざまな結果を比較することです。次に、それぞれの内容を正確に確認し、さらに最適化する対象を決定できます。しかし、アダムが彼のコメントで言ったように、あなたはおそらく非常に小さなものでパフォーマンスよりも可読性/保守性に焦点を合わせるのが最善です。
編集:ビジュアルスタジオに同梱されているILDASM.exeを使用してILを表示し、コンパイルしたアセンブリを開くことができます。
はい、違いがあります。
Visual Studio 2017 15.9.8
ターゲティング.NET Framework 4.6.1
の使用。以下のサンプルを検討してください。
static void Main(string[] args)
{
// Make sure our null value is not optimized away by the compiler!
var s = args.Length > 100 ? args[100] : null;
var foo = string.Empty;
var bar = string.Empty;
foo = s ?? "foo";
bar = s != null ? s : "baz";
// Do not optimize away our stuff above!
Console.WriteLine($"{foo} {bar}");
}
ILDasm
を使用すると、コンパイラーがそれらのステートメントを同等に処理しないことが明らかになります。
??演算子:
IL_001c: dup
IL_001d: brtrue.s IL_0025
IL_001f: pop
IL_0020: ldstr "foo"
IL_0025: ldloc.0
条件付きnullチェック:
IL_0026: brtrue.s IL_002f
IL_0028: ldstr "baz"
IL_002d: br.s IL_0030
IL_002f: ldloc.0
どうやら、??
演算子はスタック値の重複を意味します(s
変数である必要がありますか?)。簡単なテストを(複数回)実行して、感じのうち、どちらが速いかを確認しました。この特定のマシンで実行されているstring
を操作して、これらの平均数を取得しました。
?? operator took: 583 ms
null-check condition took: 1045 ms
ベンチマークサンプルコード:
static void Main(string[] args)
{
const int loopCount = 1000000000;
var s = args.Length > 1 ? args[1] : null; // Compiler knows 's' can be null
int sum = 0;
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
for (int i = 0; i < loopCount; i++)
{
sum += (s ?? "o").Length;
}
watch.Stop();
Console.WriteLine($"?? operator took {watch.ElapsedMilliseconds} ms");
sum = 0;
watch.Restart();
for (int i = 0; i < loopCount; i++)
{
sum += (s != null ? s : "o").Length;
}
watch.Stop();
Console.WriteLine($"null-check condition took {watch.ElapsedMilliseconds} ms");
}
答えはイエスです。違いがあります。
PS。 StackOverflowは、同じ文で「パフォーマンス」と「無視できる」に言及する投稿を自動警告する必要があります。時間単位が無視できるかどうかを確実に知ることができるのは、元の投稿者だけです。