LoginSignup
3
2

Dictionary に気軽に TryAdd しても良いのか

Last updated at Posted at 2024-05-15

以下のようなケースについて考えます。

Dictionary<string, object> dict = new();

dict.TryAdd("A", new());
dict.TryAdd("B", new());
dict.TryAdd("C", new());

意図通り動作するが…

TryAdd(key, new()) するとは辞書に追加されない場合もオブジェクトがインスタンス化されてるよね? っていうお話です。

テストコード

実行環境: https://dotnetfiddle.net/

using System;
using System.Collections.Generic;
using System.Threading;

public class Program
{
	public class Test
	{
        static int counter = 0;
		public Test()
		{
			Console.WriteLine("Test ctor: " + counter++);
		}
	}
	
	public static void Main()
	{
		var dict = new Dictionary<string, Test>();
		dict.TryAdd("A", new());
		dict.TryAdd("A", new());
		dict.TryAdd("A", new());

        // TryAdd での new() を避けるには・・・
        if (!dict.ContainsKey("A"))
        {
            dict.Add("A", new());
        }

		Console.WriteLine("count: " + dict.Count);

		var ilock = new Test();
		Interlocked.CompareExchange(ref ilock, new(), null);
		Interlocked.CompareExchange(ref ilock, new(), null);
		Interlocked.CompareExchange(ref ilock, new(), null);
	}
}

テスト結果は以下の通り。new() の数だけオブジェクトが作られて TryAdd CompareExchange に渡され、そして不要なら破棄されます。

条件が整った時のみ式を実行する特別扱いされたメソッドではないという事ですね。

Test ctor: 0
Test ctor: 1
Test ctor: 2
count: 1
Test ctor: 3
Test ctor: 4
Test ctor: 5
Test ctor: 6

コンパイラー側で良い感じにしてくれても良くね? と思うも勝手に式の優先順位変えるのはあり得ないか。辞書に限っては foreach の配列特別扱いと同様に ContainsKey + Add へ展開してくれても良さそうな気はする。

GitHub を見て安心感を得る

誰でもつい書いちゃうよね? という安心感を得るために公式のソースを狙い撃ちして探します。

検索ワード(検索結果への直リンクだと何故か一件も引っかからない)
language:C# /\.TryAdd\(.*,.*new.*\(\)\)/ AND (/namespace.*Microsoft/ OR /namespace.*System/)

--

オブジェクトを作るだけ作って即時破棄するようなコードパスは少ないですが、無くはないです。良かった!

Interlocked は?

こっちも同志が見つかります 😊

language:C# /Interlocked\.CompareExchange\(ref.*,.*new.*\(\),.*null\)/ AND (/namespace.*Microsoft/ OR /namespace.*System/)

おまけ

調べたときにちょいちょい見かけた Interlocked.CompareExchange を使ってダブルチェックを行うワンライナー。

return obj ?? Interlocked.CompareExchange(ref obj, new(), null) ?? obj;

new() 部分を遅延実行するような書き方は思いつかなかったし公式ソースにも見当たらなかったから多分無理?

👇 ダブルチェック

--

ConcurrentDictionaryInterlocked.CompareExchange はしょうがない(?)かもですが、普通の Dictionary での TryAdd + new() は神経質になるほどではないが気に留めておいて次からは気を付けましょう、という感じでしょうか。

以上です。お疲れ様でした。

3
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2