C# 核心 3.1 ByRef 和 ByVal

C# Core 3.1 ByRef and ByVal

提问人:Mertez 提问时间:3/19/2020 最后编辑:Kate OrlovaMertez 更新时间:3/19/2020 访问量:911

问:

我想在 C# 核心中设置一个对象等于其他对象值,而不是通过引用!

x.len=10;
val y=x;
y.len=x.len*2;

我希望设置为 20 并保持不变,但两者都是 20,我正在寻找一种断开连接的方法。y.lenx.leny.lenx.lenxy

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

C# ASP.Net-Core 按引用传递

评论

2赞 Jon Skeet 3/19/2020
听起来您想要一个值类型而不是引用类型。您尚未在此处显示任何类型,但是如果 和 是对同一对象的引用,则将获得所需的效果。请注意,这与按引用传递和按值传递是完全分开的,后者是关于如何处理方法参数/参数的。xy
0赞 Marc Gravell 3/19/2020
小问题:没有“C# Core 3.x”这样的东西 - 有 .NET Core 3.x(即将被 .NET 5.x 取代),还有“C# x.y”(目前在 C# 8.*),但没有“C# Core”;但是,是的,C# 和 .NET 具有一系列 by-val 和 by-ref 语义,包括常规 vs ref-locals、传递 by-value 与 pass-by-ref 参数以及 value-types 与 reference-types,但如果不提供更多示例来说明您在这个特定示例中实际执行的操作,就不可能知道什么适用;最重要的是:“什么是X?
0赞 Mertez 3/19/2020
我在这里找到了最好的解决方案作为扩展方法
0赞 Mrinal Kamboj 3/19/2020
您找到的解决方案是使用反射,它无法用于各种复杂对象,请检查粘贴在链接中的代码的 git 存储库问题

答:

2赞 Mrinal Kamboj 3/19/2020 #1

我想在 c# core 中设置一个对象等于其他对象值,而不是通过引用!

为此,您需要一个值类型,例如 struct,它不是在堆上分配的,或者对于引用类型,您需要一个深拷贝而不是浅拷贝。 在 .Net C# 中,默认情况下,所有类都是在堆上分配的,都是引用类型,当传递对象时,它要么按值引用(这是指向同一内存的另一个指针),要么按引用引用(指向指针的指针, 这是使用 ref 和 out 关键字)

让我们回顾一下代码

var y=x;,从结果来看,可以安全地假设 y 和 x 属于同一类型,但两者都是参考类型。因此,如果不适合您,请执行下列操作之一:value type

通过以下方式创建深层副本:

  1. 序列化,最好是二进制作为其紧凑,对于非循环对象层次结构序列化效果很好,当你反序列化时,它就是一个新对象
  2. 使用 copy 构造函数,其中对象逐个属性、逐个字段复制到新分配中

一旦你这样做了,你会发现一个对象在修改属性时不会修改另一个对象的浅层复制

评论

0赞 Mertez 3/19/2020
谢谢@Mrinal,我的对象有很多属性,我无法手动复制它
0赞 Mrinal Kamboj 3/19/2020
然后尝试序列化是最好的选择,对于 .Net 框架,有很多很好的二进制序列化程序,比如 proto-buf,由 Marc 实现,他也回答了这个问题。理想情况下,即使是复制,它最好实现,它为每个对象公开 DeepClone,并且它作为类定义的一部分完成,而不是单独完成IClone<T>
1赞 Marc Gravell 3/19/2020
澄清:protobuf 是 Google 的发明/财产;我只声称拥有 protobuf-net 的所有权,protobuf 是 protobuf 的独立实现
1赞 Marc Gravell 3/19/2020 #2

“断开”事物的最简单方法是首先让它们成为价值类型;如果是某种 ,那么你所拥有的已经可以工作了,但是:可变结构通常是一个糟糕的想法,会引起各种混乱,所以实际上我会在那里使用不同的 API:xstruct

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);
}