【问题标题】:C# Structs "this = ...."C# 结构“this = ....”
【发布时间】:2012-03-10 18:16:02
【问题描述】:

我刚刚在反射器中浏览了一个文件,并在结构构造函数中看到了这个:

this = new Binder.SyntaxNodeOrToken();

我以前没见过这个术语。有人能解释一下这个赋值在 C# 中的含义吗?谷歌很难。

【问题讨论】:

  • 除非我疯了,否则这是一个错误。
  • @ChaosPandion:要么我们都疯了,要么我同意。也许反射器弄错了?
  • @ChaosPandion 这对于 C# 中的 struct 是完全合法的
  • @JaredPar - 现在我以为我对 C# 了如指掌……当然我知道我疯了。
  • @ChaosPandion:以我的回答为例。

标签: c# struct


【解决方案1】:

它基本上取代了价值。它有效地将所有字段从右侧复制到左侧......除了即使字段是只读的,它也可以工作。是的,它确实看起来很奇怪,而且有点吓人。

例子:

using System;

class Test
{
    static void Main()
    {
        Point point = new Point(10, 20);
        point.ReplaceWith(new Point(2, 3));
        Console.WriteLine(point); // (2, 3)
    }
}

struct Point
{
    private readonly int x;
    private readonly int y;

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public void ReplaceWith(Point other)
    {
        this = other;
    }

    public override string ToString()
    {
        return string.Format("({0}, {1})", x, y);
    }
}

有关更多信息,请阅读 C# 4 规范的第 7.6.7 节,其中包括:

  • [关于它在结构构造函数中的使用的文本]

  • this 用于结构的实例方法或实例访问器内的主表达式 时,它被归类为变量。变量的类型是发生使用的结构的实例类型。

    • 如果方法或访问器不是迭代器,则this 变量表示为其调用方法或访问器的结构,其行为与结构类型的ref 参数完全相同。

    • [关于迭代器的文本]

【讨论】:

  • 这太奇怪了...... C# 压倒了我(也许还有很多其他人)的编程概念
  • @ziq:不要忘记this 是值类型中的“整个值”,而不是引用。我认为这是关键部分。
  • 如何执行?在成员函数运行时替换自己(在本例中为 ReplaceWith
  • @luiscubal:或者,就像Eric Lippert explains it,“结构中的readonly 字段在道德上相当于结构作者在没有资金支持的情况下写了一张支票。”
  • @DanielPryden new link
【解决方案2】:

如果s1s2Foo 类型的结构,具有f1f2f3 字段,则语句s1 = s2 在语义上等价于

s1.f1 = s2.f1;
s1.f2 = s2.f2;
s1.f3 = s2.f3;

除了不应该假设赋值操作的顺序(甚至读取和写入的相对顺序;例如,生成的代码可能会将所有三个字段读入寄存器,然后写入所有三个字段) .所有字段都将被复制,无论它们是公共的还是私有的、可变的还是所谓的不可变的。不会调用任何属性 getter 或 setter;源结构体和目标结构体都不会收到任何关于结构体的字段被复制或覆盖的通知。

声明 this = new Foo(whatever); 在 C#(*) 中等同于

Foo temp;
call Foo's constructor (out temp, whatever);
this.f1 = temp.f1;
this.f2 = temp.f2;
this.f3 = temp.f3;

(*) vb.net中的struct构造函数语义不同

如上所述,字段分配不考虑字段是公共的还是私有的,也不考虑它们是否应该是不可变的。

我认为(与其他人的观点相反)结构应该经常公开可变字段的一个原因是这样的语法:

// Assume myKVP is a field of type KeyValuePair<Wizzle, int>
rr = new KeyValuePair<Wizzle, int>(myKVP.Key, myKVP.Value + 1);

使它看起来好像myKVP 将在分配后引用与之前不同的实例,而实际发生的是:

// Assumes backing fields are named _Key and _Value
// Note that C# won't allow one to write private fields directly, but the
// act of copying one struct instance to another copies all the fields,
// public and private, from the source instance to the destination.
KeyValuePair<Wizzle, int> temp;
temp._Key = myKVP.Key; // Constructor has access to backing fields
temp._Value = myKVP.Value+1;
myKVP._Key = temp._Key; // Struct assignment copies all fields, public and private
myKVP.Value = temp.Value;

换句话说,该语句不会使myKVP 报告给不同的实例;相反,它会创建一个新实例,通过用新实例的字段覆盖其字段来变异旧实例,然后丢弃新实例。如果在上述赋值发生时某些代码正在评估 myKVP.ToString(),则突变会影响正在打印的 myMVP 实例。

结构可以具有有用的语义,但所谓的“不可变”结构则不然。当且仅当它们保存在可变的存储位置中时,非平凡结构(可以为其创建不同于默认值的结构)是可变的,而不管类型强加的任何语义。自变异结构,即在除构造函数和属性设置器之外的任何方法中变异 this 的结构,可能会出现一些意想不到的行为,因为编译器无法禁止调用其他将在不可变结构上变异 this 的方法 实例。但是,公开暴露结构字段不会造成任何此类危险。由于非平凡结构的可变实例的所有字段本质上都是可变的,无论结构可能进行任何允许突变的尝试,并且结构的不可变实例的所有字段即使它们被暴露也是不可变的,因此结构努力使其领域“不可变”的公司实际上是在撒谎。有时可能有用的谎言(​​例如,如果字段内容应该服从某些不变量)但不应该在没有真正原因的情况下说出来。

【讨论】:

  • 我不同意你的论点。您的 myKVP 语法示例令人困惑,似乎不遵循 C# 语法。如果一个结构是不可变的,那么为什么它会包含“其他将改变this的方法”?在您的最后一段中,关于 value 类型 和该类型的 instances 之间的区别似乎有些混淆。您能否编辑您的答案以更好地解释您要说的内容?
  • @DanielPryden:我刚刚添加了几个 cmets 并试图澄清后一段。最初的问题是为什么 can 用另一个结构替换 this。之所以能做到这一点,是因为它并没有真正取代this;一个只是将其所有字段的内容替换为另一个结构的内容。然后我想说,一个人可以改变“this”这一事实,无论是通过覆盖整个事物还是改变它的字段,并不意味着应该。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-18
相关资源
最近更新 更多