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 设置为 20x.len 保持不变,但 y.lenx.len 都是 20,我正在寻找断开xy 的方法。

我记得在 VB6 中我们有类似 ByRefByVal 的东西,它们让我们能够控制这个东西,有没有类似的C# Core 3.x 中的东西?

【问题讨论】:

听起来你想要一个值类型而不是引用类型。您在这里没有显示任何类型,但如果 xy 是对同一个对象的引用,您获得您想要的效果。请注意,这与传递引用和传递值完全不同,后者是关于如何处理方法参数/参数 次要问题:没有“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的主要内容,如果未能解决你的问题,请参考以下文章

ByVal 和 ByRef 的区别?

VBA中ByVal和 ByRef有啥区别?

Visual Basic 6.0中ByVal和ByRef的区别和应用示例!optional和缺省时的区别和应用示例!

JavaScript 传播语法与 jQuery $.extend - ByRef 和 ByVal

Objective-C in,out,inout,byref,byval, .. 等等。这些是啥?

为啥 ByRef 不能与 WithEvents 一起使用?