访问结构体字段复制整个结构体



我知道将结构体作为参数传递给方法,返回它们,或者尝试将结构体值赋给变量会创建整个结构体的副本。

现在,看看下面的假设代码:
//Regions is an array of a struct type. Color and r/g/b are structs too.
if(Regions[0].Color.r == Regions[0].Color.g && Regions[0].Color.b != 0){
  .
  ..      
}

要得到r/g/b值,下面发生了什么?只有r/g/b值被复制到内存位置或整个区域[0]三次?

我认为这段代码复制了你的问题:

void Main()
{
    var foos = new Foo[]
    {
        new Foo() { Color = System.Drawing.Color.Red },
    };
    if (foos[0].Color.R == 45 && foos[0].Color.B == 55)
    {
        Console.WriteLine("!");
    }
}
public struct Foo
{
    public System.Drawing.Color Color;
}

产生的IL是:

<>之前IL_0000: ldc.i4.1[00:01 . 00] [qhIL_0006: dupIL_0007: ldc.i4.0IL_0008: ldloca。年代01IL_000A: initobj FooIL_0010: ldloca。年代01IL_0012:调用System.Drawing.Color.get_RedIL_0017: stfld Foo。颜色IL_001C: ldloc.1IL_001D: stelem FooIL_0022: stloc。0//fooIL_0023: ldloc。0//fooIL_0024: ldc.i4.0IL_0025: ldelema Foo[au:] [au:]颜色IL_002F:调用System.Drawing.Color.get_RIL_0034: ldc.i4。年代2 dIL_0036: bne.un.s IL_0057IL_0038: ldloc。0//fooIL_0039: ldc.i4.0IL_003A: ldelema Foo[au:] [au:]颜色IL_0044:调用System.Drawing.Color.get_BIL_0049: ldc.i4。年代37IL_004B: bne.un.s IL_0057IL_004D: "!"IL_0052:调用System.Console.WriteLineIL_0057:受潮湿腐烂之前

您将注意到ldelemaldflda各有两批op码。两者都只是传递引用。

所以,不需要复制两个副本。

Region是一个结构体数组。我做了一个快速示例来检查生成的IL:

public class Program
{
    public static void Main()
    {
        Foo[] Regions = new Foo[10];
        Regions[0] = new Foo();

        if(Regions[0].Color.R == Regions[0].Color.G && Regions[0].Color.B != 0)
        {
        }

    }
}
public struct Foo
{
    public Color Color { set; get; }
}

正如您在下面生成的IL中看到的那样,get_Color被调用了3次,因此Color的值被复制了3次并被检索。

  .method public hidebysig static void  Main() cil managed
  {
    // 
    .maxstack  3
    .locals init (valuetype Foo[] V_0,
             valuetype [System.Drawing]System.Drawing.Color V_1,
             bool V_2)
    IL_0000:  nop
    IL_0001:  ldc.i4.s   10
    IL_0003:  newarr     Foo
    IL_0008:  stloc.0
    IL_0009:  ldloc.0
    IL_000a:  ldc.i4.0
    IL_000b:  ldelema    Foo
    IL_0010:  initobj    Foo
    IL_0016:  ldloc.0
    IL_0017:  ldc.i4.0
    IL_0018:  ldelema    Foo
    IL_001d:  call       instance valuetype [System.Drawing]System.Drawing.Color Foo::get_Color()
    IL_0022:  stloc.1
    IL_0023:  ldloca.s   V_1
    IL_0025:  call       instance uint8 [System.Drawing]System.Drawing.Color::get_R()
    IL_002a:  ldloc.0
    IL_002b:  ldc.i4.0
    IL_002c:  ldelema    Foo
    IL_0031:  call       instance valuetype [System.Drawing]System.Drawing.Color Foo::get_Color()
    IL_0036:  stloc.1
    IL_0037:  ldloca.s   V_1
    IL_0039:  call       instance uint8 [System.Drawing]System.Drawing.Color::get_G()
    IL_003e:  bne.un.s   IL_0059
    IL_0040:  ldloc.0
    IL_0041:  ldc.i4.0
    IL_0042:  ldelema    Foo
    IL_0047:  call       instance valuetype [System.Drawing]System.Drawing.Color Foo::get_Color()
    IL_004c:  stloc.1
    IL_004d:  ldloca.s   V_1
    IL_004f:  call       instance uint8 [System.Drawing]System.Drawing.Color::get_B()
    IL_0054:  ldc.i4.0
    IL_0055:  ceq
    IL_0057:  br.s       IL_005a
    IL_0059:  ldc.i4.1
    IL_005a:  nop
    IL_005b:  stloc.2
    IL_005c:  ldloc.2
    IL_005d:  brtrue.s   IL_0061
    IL_005f:  nop
    IL_0060:  nop
    IL_0061:  ret
  } // end of method Program::Main

最新更新