【发布时间】: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# 实现中的一个错误。