如果我内联一个 HashSet 集合初始化,编译器是否知道只执行一次?

If I inline a HashSet collection initialization, does the compiler know to do it only once?

我经常这样做...

private void Check()
 {
 string s = "blah";

 if ( new HashSet<string>{"Joe","Eddie","Buckethead"}.Contains(s) )
   Debug.Log("Guitarist.");
 }

pipeline中,HashSet是否真的只创建一次(启动时?编译时?)然后每次都使用?

顺便说一句,我假设如果你这样做:

private HashSet<string> g = new HashSet<string>()
                         {"Joe","Eddie","Buckethead"};
private void Check()
 {
 string s = "blah";

 if ( g.Contains(s) )
  Debug.Log("Guitarist.");
 }

那么确实,当然只在实例化Class的时候做一次。 (或者,也许在编译时/启动时?但无论如何,只有一次。)

new 将在运行时恰好在线程到达该行的时间点创建新对象。

因此对于第一个代码段,每次调用 Check 方法都会导致创建一个新的 HashSet 对象。

在第二种情况下,每次构造包含 class 的新实例时都会创建一个新的 HashSet 对象。

这个代码

{
    string s = "blah";

    if ( new HashSet<string>{"Joe","Eddie","Buckethead"}.Contains(s) )
       Debug.Log("Guitarist.");
}

总是实例化一个新的 HashSet<string>

顺便说一句,新对象一旦不再被引用就可用,在本例中是在您关闭后 }

你说得对,当 class 初始化时,将其作为 class 实例字段将初始化一次。在这种情况下,您可以使用 readonly 关键字来防止 class 在对象初始化后更改初始值。如果您有一个永远不会更改的昂贵初始化并且可能有许多 class 实例,您可以将该字段标记为 static 以便 HashSet<string> 的单个实例在所有对象实例之间共享.

hmm .. ok but compilers know to only make once other literals, right? (eg, "strings" etc)

常量不会产生任何初始化开销。编译器可以在适当的地方使用文字值。

默认string literals are interned (see also),这意味着给定字符串的内存只会分配一次。

这是第一个方法的 IL:

Check:
IL_0000:  ldstr       "blah"
IL_0005:  stloc.0     // s
IL_0006:  newobj      System.Collections.Generic.HashSet..ctor
IL_000B:  dup         
IL_000C:  ldstr       "Joe"
IL_0011:  callvirt    System.Collections.Generic.HashSet.Add
IL_0016:  pop         
IL_0017:  dup         
IL_0018:  ldstr       "Eddie"
IL_001D:  callvirt    System.Collections.Generic.HashSet.Add
IL_0022:  pop         
IL_0023:  dup         
IL_0024:  ldstr       "Buckethead"
IL_0029:  callvirt    System.Collections.Generic.HashSet.Add
IL_002E:  pop         
IL_002F:  ldloc.0     // s
IL_0030:  callvirt    System.Collections.Generic.HashSet.Contains
IL_0035:  brfalse.s   IL_0041
IL_0037:  ldstr       "Guitarist."
IL_003C:  call        System.Console.WriteLine
IL_0041:  ret         

这是第二种方法的代码:

Check:
IL_0000:  ldstr       "blah"
IL_0005:  stloc.0     // s
IL_0006:  ldarg.0     
IL_0007:  ldfld       g
IL_000C:  ldloc.0     // s
IL_000D:  callvirt    System.Collections.Generic.HashSet.Contains
IL_0012:  brfalse.s   IL_001E
IL_0014:  ldstr       "Guitarist."
IL_0019:  call        System.Console.WriteLine
IL_001E:  ret         

这就是代码的优化编译。

所以,是的,第一个每次都会创建一个新的 HashSet

哦,我将 Debug.Log 更改为 Console.WriteLine,但这是一个微不足道的更改。