结构类型数组的性能

red*_*alx 8 c# arrays performance struct

例:

// Potentially large struct.
struct Foo
{ 
   public int A;
   public int B;
   // etc.
}

Foo[] arr = new Foo[100];
Run Code Online (Sandbox Code Playgroud)

如果Foo是100字节结构,则在执行以下语句期间将在内存中复制多少字节:

int x = arr[0].A

也就是说,将arr [0]计算为某个临时变量(Foo实例的100字节副本),然后将.A复制到变量x(4字节副本).

或者是编译器,JITer和CLR的某种组合能够优化此语句,以便将4个字节A直接复制到x.

如果执行了优化,当项目被保存在一个List<Foo>数组中或者数组作为一个IList<Foo>或一个数组传递时,它是否仍然存在ArraySegment<Foo>

Eri*_*ert 10

值类型按值复制 - 因此名称.那么我们必须考虑必须在何时复制一个值.这归结为在特定实体引用变量时正确分析.如果它引用了一个值,那么该值就是从某个地方复制的.如果它引用变量,那么它只是一个变量,并且可以像任何其他变量一样对待.

假设我们有

struct Foo { public int A; public int B; }
Run Code Online (Sandbox Code Playgroud)

暂时忽略设计缺陷; 公共领域是一种糟糕的代码气味,可变结构也是如此.

如果你说

Foo f = new Foo();
Run Code Online (Sandbox Code Playgroud)

怎么了?规范说:

  • f创建一个新的八字节变量.
  • temp创建临时的八字节存储位置.
  • temp 用八个字节的零填充.
  • temp被复制到f.

但这不是实际发生的事情; 编译器和运行时很聪明,可以注意到所需工作流和工作流"创建f并用零填充"之间没有可观察到的差异,因此发生这种情况.这是一个复制省略优化.

练习:设计一个编译器无法复制的程序,并且输出清楚地表明编译器在初始化struct类型的变量时不执行复制省略.

现在,如果你说

f.A = 123;
Run Code Online (Sandbox Code Playgroud)

然后f评估产生一个变量 - 而不是一个值 - 然后从中A计算产生一个变量,并将四个字节写入该变量.

如果你说

int x = f.A;
Run Code Online (Sandbox Code Playgroud)

然后f被评估为变量,A被评估为变量,并且值A被写入x.

如果你说

Foo[] fs = new Foo[1];
Run Code Online (Sandbox Code Playgroud)

然后fs分配变量,分配数组并用零初始化,并将对数组的引用复制到fs.当你说

fs[0].A = 123;
Run Code Online (Sandbox Code Playgroud)

和之前一样.f[0]被评估为变量,因此A变量,因此将123复制到该变量.

当你说

int x = fs[0].A;
Run Code Online (Sandbox Code Playgroud)

和以前一样:我们评估fs[0]为变量,从该变量中获取值的值A,然后复制它.

但如果你说

List<Foo> list = new List<Foo>();
list.Add(new Foo());
list[0].A = 123;
Run Code Online (Sandbox Code Playgroud)

然后你会得到编译器错误,因为它list[0]是一个,而不是一个变量.你无法改变它.

如果你说

int x = list[0].A;
Run Code Online (Sandbox Code Playgroud)

然后list[0]被评估为一个值 -存储在列表中的值的副本是由-然后副本A中制成x.所以这里有一个额外的副本.

练习:编写一个程序,说明它list[0]是存储在列表中的值的副本.

出于这个原因,你应该(1)不要制作大结构,(2)使它们不变.结构被值复制,这​​可能很昂贵,而值不是变量,所以很难改变它们.

是什么让数组索引器返回变量但列表索引器不?阵列是以特殊方式处理的吗?

是.数组是非常特殊的类型,它们深深地构建在运行时中,并且从版本1开始.

这里的关键特性是数组索引器在逻辑上产生数组中包含的变量别名 ; 然后,该别名可以用作变量本身.

所有其他索引器实际上是get/set方法对,其中get返回一个值,而不是一个变量.

在这方面,我可以创建自己的类来表现与数组相同的行为

在C#7之前,不在C#中.您可以在IL中执行此操作,但当然C#不知道如何处理返回的别名.

C#7增加了将别名ref返回变量的方法的能力:返回.请记住,ref(和out)参数将变量作为其操作数,并使被调用者具有该变量的别名.C#7还为本地人回归添加了这样做的能力.

  • 是什么让数组索引器返回变量但列表索引器不?阵列是以特殊方式处理的吗?在这方面,我可以创建自己的类来表现与数组相同吗? (2认同)