更改值类型的“this”变量

Posted

技术标签:

【中文标题】更改值类型的“this”变量【英文标题】:Changing the 'this' variable of value types 【发布时间】:2013-01-23 18:25:36 【问题描述】:

显然,您可以从结构中的任何位置(但不能在类中)更改 this 值:

struct Point

    public Point(int x, int y)
    
        this = new Point();
        X = x; Y = y;
    
    int X; int Y;

我以前从未见过,也从未需要它。为什么有人想要这样做? Eric Lippert reminds us 必须有理由实施一项功能。什么伟大的用例可以证明这一点?有没有什么场景是无价的? 我找不到任何关于它的文档1

此外,对于调用构造函数,已经有一种更广为人知的替代语法,所以这个特性有时是多余的:

public Point(int x, int y)
    : this()

    X = x; Y = y;

我在 Jeffrey Richter 的 CLR via C# 4th edition 的示例中发现了此功能。1) 显然它在 C# specification 中。

【问题讨论】:

如果构造函数是私有的怎么办?并不是说这是一个很棒的主意……但我想这是一个用例。 它似乎是对象的廉价替代品? 任何让我想去的问题,“没门!让我们通过 LinqPad 运行它。”在我的书中是一个很棒的。 对于@EricLippert 来说似乎是一个完美的机会来说明此功能的合理性。 我想我找到了一个用例。在一个方法中,您可以将结构“归零”。 (+1 发布这个,不敢相信 11 年后我仍然学习新的 c# 东西)。编辑:在构造函数中使用它是毫无意义的。 【参考方案1】:

好问题!

根据定义,值类型是按值复制的。如果this 实际上不是存储位置的别名,那么构造函数将初始化一个副本,而不是初始化您要初始化的变量。这将使构造函数变得不那么有用!对于方法也是如此;是的,可变结构是邪恶的,但如果你要再次创建一个可变结构,this 必须是正在发生突变的变量,而不是其值的副本。

您所描述的行为是该设计决策的逻辑结果:由于 this 为变量取别名,您可以分配给它,就像您可以分配给任何其他变量一样。

像这样直接分配给this,而不是分配给它的字段有点奇怪。更奇怪的是直接分配给this,然后覆盖 100% 的分配!

另一种避免使this 成为接收者存储的别名的设计是从短期存储池中分配this,在ctor 中对其进行初始化,然后按值返回它。这种方法的缺点是它使复制省略优化几乎不可能,并且它使 ctors 和方法奇怪地不一致。

【讨论】:

但即便如此,this 也可以设为只读以与引用类型保持一致,只允许对其字段进行分配。能够直接写信给this 有什么用,或者只是将this 当作别名处理的副作用有点少? @Virtlink 对于引用类型,readonly 将引用设为只读,但您仍然可以修改其字段。但是对于值类型,没有引用,所以readonly 实际上使所有字段都是只读的。所以你的提议没有多大意义。 @svick First:我不是指readonly 关键字。第二:值类型中的this实际上是ref thisout this,据我了解,所以它实际上是一个引用(指向值类型的存储位置)。 @Virtlink:假设语言设计团队采纳了你的建议。如果有人在 ctor 中调用 M(out this),你建议会发生什么?在这种情况下,this 是否也应该被视为不是变量 @Virtlink:您将值类型作为 out 传递 因为您希望它们由被调用者初始化。假设 ctor writer 已经有一个方法可以正确初始化该类型的变量;他们为什么不使用它?【参考方案2】:

另外,我找不到任何关于它的文档。

您是否尝试查看 C# 规范?因为我可以找到关于它的文档(7.6.7):

this 用于结构的实例构造函数中的primary-expression 时,它被归类为变量。变量的类型是发生使用的结构的实例类型(第 10.3.1 节),变量代表正在构造的结构。 struct 的实例构造函数的 this 变量的行为与 struct 类型的 out 参数完全相同 - 特别是,这意味着必须在实例构造函数的每个执行路径中明确分配该变量。

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

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

至于它的用例,我想不出很多——我唯一能想到的就是如果你想在构造函数中分配的值计算起来很昂贵,而且你有一个你想复制到this的缓存值,这可能很方便。

【讨论】:

对于修改this的结构成员,实际上会有相当多的使用案例,除了C#和vb.net愿意允许(没有任何编译器诊断)这样的无效调用只读结构实例上的方法,并且缺乏任何标记可以在只读实例上调用哪些方法和属性的方法,这并不意味着使用相对丑陋的形式StructType.StaticMethod(ref theStruct) 更安全,编译器将正确禁止这种形式只读结构。【参考方案3】:

在包含该类型的公共和私有字段的存储位置聚合中的值类型的存储位置。将值类型传递给普通(值)参数将在物理上和语义上传递其所有字段的内容。将值类型作为ref 参数传递在语义上是传递其所有字段的内容,尽管使用单个“byref”来传递所有字段。

在结构上调用方法相当于将结构(以及它的所有字段)作为ref 参数传递,除了一个皱纹:通常,C# 和 vb.net 都不允许只读值作为ref 参数传递。但是,两者都允许在只读值或临时值上调用结构方法。他们通过复制所有结构(以及它的所有字段)来做到这一点,然后将该副本作为ref 参数传递。

由于这种行为,有些人将可变结构称为“邪恶”,但唯一的邪恶是 C# 或 vb.net 都没有定义任何属性来指示结构成员或属性是否应该在ref不能直接传递。

【讨论】:

以上是关于更改值类型的“this”变量的主要内容,如果未能解决你的问题,请参考以下文章

理解栈堆值类型引用类型装箱和拆箱

JavaScript高级程序设计(复制变量值传递参数)

是否可以在不指定类型的情况下将变量值打印到调试?

Java基础中Int类型变量值互换的几种方法

linux12shell编程 -->变量值操作

如何在 Long 数据类型变量中设置 Optional<Long> 数据类型变量值? [关闭]