C#中的自赋值
Posted
技术标签:
【中文标题】C#中的自赋值【英文标题】:Self assignment in C# 【发布时间】:2013-09-19 21:04:42 【问题描述】:我正在查看我不久前编写的一些代码,并意识到我对 C# 中的赋值运算符做了一个假设。这是有问题的代码行(它按预期工作):
pointsChecked = pointsChecked ?? new List<Point>();
pointsChecked
是一个列表,指定为递归函数的参数。它是一个默认参数,默认值为null
。我想要做的是初始化一次,然后构建一个我已经检查过的点的集合,所以它应该只在第一次迭代期间被初始化。
我的假设是 C# 可以防止自赋值,就像 C++ operator=
应该在重载时提供保护(即if(this == &rightHandSide) return *this;
)一样。但是,我无法找到任何明确声明这适用于 C# 的资源。
我找到的最接近的例子是this question about the null-coalescing operator,如果对象不是null
,它似乎会被分配回自身。在该示例中没有人对自我分配发表任何评论,但我想确定这不是一个坏做法,并且没有负面影响。
Searching on MSDN我还发现(根据我的理解解释)右侧的值被复制到左侧的值并返回。所以我再次不确定进行自我分配是否是一件坏事。
我知道我可以采取以下措施更安全:
if(pointsChecked == null)
pointsChecked = new List<Point>();
但我更愿意了解自我分配实际发生的情况。
【问题讨论】:
尝试优化字段(不是普通的局部变量)的自赋值可能会对多线程代码产生一些不小的影响。不确定 CLR 内存模型是否允许这种优化。 C# 不是 C++。在重载赋值运算符中防止自赋值的整个想法毫无意义。当一个包含它们的变量被分配给时,对象不能发生变异,它要么是对指向其他地方的对象的引用,要么是值类型的内容被该类型的另一个实例的内容覆盖。 @millimose 我意识到这一点。我不是在问如何防止自赋值,你甚至不能重载 C# 中的赋值运算符。我是说我假设在幕后存在类似的结构,并希望对实际发生的事情进行一些澄清。 @prettycooldevguy 在幕后,pointsChecked
变量的存储桶将保存其原始值,或者如果原始值为 0
,则为 List
的对象句柄。原始值是否自分配给自己并不重要,因为它只是一个指针或不透明的对象引用。不管发生什么都不重要——如果这对你来说不是完全透明的,只要 C# 中不存在自赋值的概念,这将是 C# 实现中的一个错误。
【参考方案1】:
Assignment 复制对对象的引用,而不是对象的内容。从来没有可定制的代码作为分配给包含对象引用的变量的一部分运行。对于结构也是如此。
在 C++ 中赋值是可自定义的,而在 C# 中则不是。
将相同的对象引用分配给已经持有它的变量是安全的:
object someRef = new object();
someRef = someRef; //always does nothing
这与分配任何其他值一样安全:
int someVal = 123;
someVal = someVal; //always does nothing
请注意,不存在克隆/复制对象的通用方法。任何依赖这种机制存在的解释都是错误的。
自赋值是安全的,因为它转换为以下近似 IL 指令:
ldloc someRef
stloc someRef
这有明确定义的语义。它首先将someRef
加载到堆栈中,然后将堆栈中的任何内容存储到someRef
。
【讨论】:
有趣,您能否详细说明当您在结构上重载operator =
时会发生什么以及它可能(或不能)如何影响这一点?
赋值运算符不能重载。既不适用于 ref 类型,也不适用于结构。 struct copy 是指定的 IL 指令,它指示运行时执行按位复制。
啊,我明白了。向您展示我尝试重载运算符的频率。
+1。请注意,“C++ 赋值是可定制的”是不完整的 - C++ - 对指针的赋值不能被重载(指向对象的指针代表了 SomeNonValueType someVar
中的 someVar
本质上是什么)......
谢谢,我想我只是想多了。很高兴知道,我还没有看到关于 SO 的其他解释,所以希望这对其他人也有帮助!以上是关于C#中的自赋值的主要内容,如果未能解决你的问题,请参考以下文章