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还为本地人和回归添加了这样做的能力.