价值类型的分配

本文关键字:分配 类型 | 更新日期: 2023-09-27 17:57:48

当您将一个值类型的实例分配给另一个实例时,对象会被逐位复制到目标位置:

private struct Word
{
    public Word(char c) { ... }
}
public void Method(Word a)
{
    Word b = a; //a is copied and stored in b
}

但给定以下代码:

private Word _word;
public void Method() {
    _word = new Word('x');
}

我怀疑首先计算右侧(RHS)表达式(它实例化堆栈上的值类型),然后将值复制并存储在堆上_word字段的位置。

另一种选择是考虑左侧,直接在_word上实例化值类型,避免复制对象。

我的怀疑正确吗?如果是这样的话,我想可以肯定地假设第一块代码的性能会比第二块好。

//1 instantiation + 10k copies
Word[] words = new Word[10000];
Word word = new Word('x');
for (int i = 0; i < 10000; i++)
    words[i] = word;

//10k instantiations + 10k copies
Word[] words = new Word[10000];
for (int i = 0; i < 10000; i++)
    words[i] = new Word('x');

注意:我并没有试图对任何东西进行微观优化。

编辑:正如Lee所说,我问题的核心是:结构是直接分配到位的,还是需要分配然后复制?

价值类型的分配

当您将一个值类型的实例分配给另一个实例时,对象会被逐位复制到目标位置

将值类型的实例分配给同一类型的变量时,值将复制到目标位置,是的。但引用类型也是如此:引用被逐位复制到目标位置。引用当然会保持原样。

我怀疑右侧(RHS)表达式是首先评估的

规范规定,对左手边进行求值以生成变量,然后对右手边进行求值来生成值,然后进行赋值。

在你给出的例子中,左手边的评估不会产生可观察到的副作用,因此它的评估可以由C#编译器中的优化器、抖动或CPU(如果他们中的任何一个选择的话)重新排序。但如果你有类似的东西

x[i++] = v();

则左手侧的副作用必须在右手侧的呼叫之前发生。

我问题的核心是:结构是直接分配到位的,还是需要分配然后复制?

规范规定,结构被分配在一个临时位置——在实践中通常是堆栈或寄存器——然后复制到它们的最终目的地。然而,在某些情况下,优化器可以确定用户不可能注意到突变是否在最终目的地"原位"发生。这是一个复制省略优化,如果C#编译器认为可以逃脱惩罚,它将执行此优化。

有关更多详细信息,请参阅我关于主题的文章:

http://ericlippert.com/2010/10/11/debunking-another-myth-about-value-types/

两者都是首选吗?

多个相同结构的业务案例是什么
如果你需要多个相同的对象,一个结构是最好的选择吗?

以这种方式初始化的结构可能不是示例用例的好解决方案
在新数组中,WordStruct使用默认的ctor(无ctor)进行分配和初始化
您没有用另一个ctor初始化结构数组的选项
如果你确实需要相同的结构,那么这将是首选的

WordStruct[] WordStructS = new WordStruct[1000];
for (int i = 0; i < WordStructS.Length; i++) { WordStructS[i].C = 'x'; }

如果你真正需要做的是多个相同的对象,那么考虑一个类
类新数组已分配但尚未初始化
使用默认构造函数初始化不会浪费资源

WordClass[] WordClassS = new WordClass[1000];
for (int i = 0; i < WordClassS.Length; i++) { WordClassS[i] = new WordClass('x'); }

如果您想泛化对象(结构或类)的深层副本,请考虑IConable
在结构的情况下,我怀疑它比逐位复制更有效(但我不是肯定的)
在类的情况下,它将使克隆(深度复制)而不是引用

public struct WordStruct : ICloneable 
{
    public char C;
    public WordStruct(char C) 
    {
        this.C = C;
    }
    public object Clone()
    {
        WordStruct newWordStruct = (WordStruct)this.MemberwiseClone();
        return newWordStruct;
    }
}

我知道你在评论中说了好奇,但这在问题中并不清楚
在这个问题中,你说第一块代码比第二块更可取

我知道这是一个有趣的好奇心问题
但如果只是好奇,那么问题应该停止在
我的怀疑正确吗?