C# Core 3.1 ByRef 和 ByVal
Posted
技术标签:
【中文标题】C# Core 3.1 ByRef 和 ByVal【英文标题】:C# Core 3.1 ByRef and ByVal 【发布时间】:2020-06-30 13:54:51 【问题描述】:我想在 C# 核心中设置一个与其他对象值相等的对象,而不是通过引用!
x.len=10;
val y=x;
y.len=x.len*2;
我希望 y.len
设置为 20 和 x.len
保持不变,但 y.len
和 x.len
都是 20,我正在寻找断开x
和y
的方法。
我记得在 VB6 中我们有类似 ByRef 和 ByVal 的东西,它们让我们能够控制这个东西,有没有类似的C# Core 3.x 中的东西?
【问题讨论】:
听起来你想要一个值类型而不是引用类型。您在这里没有显示任何类型,但如果x
和 y
是对同一个对象的引用,您将获得您想要的效果。请注意,这与传递引用和传递值完全不同,后者是关于如何处理方法参数/参数。
次要问题:没有“C# Core 3.x”之类的东西 - 有 .NET Core 3.x(即将被 .NET 5.x 取代),还有“C# xy” (目前在 C# 8.*),但没有“C# Core”;但是,是的,C# 和 .NET 具有一系列 by-val 和 by-ref 语义,包括常规 vs ref-locals、pass-by-value vs pass-by-ref 参数,以及 value-types vs reference-types,但它如果没有更多关于您在这个特定示例中实际执行的示例,就不可能知道什么适用;最重要的是:“什么是 x?”
我找到了最好的解决方案here作为扩展方法
您找到的解决方案是使用反射,但它对于各种复杂对象都失败了,检查粘贴在链接中的代码的 git 存储库问题
【参考方案1】:
我想在 c# 核心中设置一个对象等于其他对象的值,而不是通过引用!
为此,您需要一个像 struct 这样的值类型,它不是在堆上分配的,或者对于引用类型,您需要一个深拷贝而不是浅拷贝。在 .Net C# 中,所有类默认分配在堆上,是引用类型,当传递对象时,它要么按值引用(这是指向同一内存的另一个指针),要么按引用引用(指向指针的指针,它使用 ref和 out 关键字)
让我们回顾一下代码
var y=x;
,查看结果,可以安全地假设 y 和 x 属于同一类型,但都是引用类型。因此,如果value type
不适合您,请执行以下操作之一:
通过以下方式创建深层副本:
-
序列化,最好是二进制作为其紧凑型,对于非循环对象层次结构序列化效果很好,当您反序列化时,它是一个新对象
使用复制构造函数,将对象逐个属性、逐个字段复制到新分配中
一旦你这样做了,你会发现与浅拷贝不同,当属性被修改时,一个对象不会修改另一个对象
【讨论】:
感谢@Mrinal,我的对象有很多属性,我无法手动复制它 那么尝试序列化是最好的选择,.Net 框架有很多好的二进制序列化器,比如 proto-buf,由 Marc 实现,他也回答了这个问题。理想情况下,即使是最好的复制也可以实现IClone<T>
,它为每个对象公开 DeepClone,并将其作为类定义的一部分而不是单独完成
澄清:protobuf 是 Google 的发明/财产;我只声明对 protobuf-net 的所有权,这是 protobuf 的独立实现【参考方案2】:
“断开”事物的最简单方法是首先将它们作为值类型;如果x
是某种struct
,那么您所拥有的已经可以工作了,但是:可变结构通常是一个糟糕的想法并且会导致各种混乱,所以实际上我会去使用不同的 API:
var x = new SomeReadonlyValueType(10);
var y = x; // this is a value copy
y = y.WithLength(x.len * 2); // mutation method
如果您不想那样做,并且希望 x
成为可变类的实例,则需要对实例进行深度克隆,即
x.len=10;
val y=x.DeepClone();
y.len=x.len*2;
在这里,它们分开的地方非常清楚和明确;不过,您需要实现DeepClone()
- 没有标准的内置方式来提供它。对于简单的情况,手卷很好;对于复杂的情况,序列化是一种流行的方式。
SomeReadonlyValueType
的示例代码:
readonly struct SomeReadonlyValueType
public int Length get;
public int Width get;
public SomeReadonlyValueType(int length, int width)
Length = length;
Width = width;
public override string ToString() => $"Length x Width";
public SomeReadonlyValueType WithLength(int length)
=> new SomeReadonlyValueType(length, Width);
public SomeReadonlyValueType WithWidth(int width)
=> new SomeReadonlyValueType(Length, width);
【讨论】:
以上是关于C# Core 3.1 ByRef 和 ByVal的主要内容,如果未能解决你的问题,请参考以下文章
Visual Basic 6.0中ByVal和ByRef的区别和应用示例!optional和缺省时的区别和应用示例!
JavaScript 传播语法与 jQuery $.extend - ByRef 和 ByVal