【问题标题】:Replacing an object on so that all references to it are kept替换一个对象,以便保留对它的所有引用
【发布时间】:2018-02-19 09:04:11
【问题描述】:

我想了解更多关于 ref 关键字的工作原理,所以我做了以下实验:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        var main = new Main { Property = 1 };
        var dependent = new Dependent(main);

        void ChangeRef(ref Main Oldmain, Main newMain)
        {
            Oldmain = newMain;

        }

        ChangeRef(ref main, new Main { Property = 5 });

        Assert.AreEqual(5,dependent.Main.Property);
    }
}

public class Main
{
    public int Property { get; set; }
}

public class Dependent
{
    public Dependent(Main main)
    {
        Main = main;
    }

    public Main Main { get; set; }
}

如您所见,我希望能够在保留引用的同时替换 main 引用的对象,但测试失败并且值仍然为 1。有人可以详细说明为什么这不起作用或指出我到可以阅读更多内容的地方?

更新: 就像下面有人回答的那样,但后来被删除了。 如果我通过引用构造函数中的依赖项来传递主对象,为什么它不起作用? 他们不应该都有相同的参考吗?

【问题讨论】:

  • 您创建了一个Main。您创建了一个 Dependent 并引用了该 Main。然后创建一个全新的Maindependent 看不到 Maindependent 没有对 main variable 的引用 - 它对 main variableobject 的引用指向那个时间点。如果您将main 变量 指向不同的对象(就像您在此处所做的那样),dependent 不会神奇地知道它。 现在,如果 ChangeRef 没有分配Oldmain 但内部更改了其属性,那么一切都会按您的预期工作。
  • 因为都是引用类型。您创建 main 指向内存位置 #1,然后创建dependent.Main 指向相同的内存位置 #1。然后您创建了指向内存位置#2 的新 Main { Property = 5},然后将 main 分配给指向 #2。但是dependent.Main 仍然指向#1。
  • 好的,但是有什么方法可以替换依赖对象指向的对象,而无需更改它们的所有属性?
  • Ok, but is there any way that I can replace the object that dependent objects are pointing at without just changing all their properties?
  • @mjwills 不,它不会工作。我有其他想法,因为我试图解释指向可能令人困惑的内存...谢谢您的指点。

标签: c# ref


【解决方案1】:

正如其他人所指出的,您不能立即使程序中的所有变量和字段都指向不同的实例。

但是,如果您想反映程序所有部分的更改,最简单的方法是将其包装在不同的类中(例如您的 Dependent 类)。然后您可以与程序的其他部分共享该类,并改为更改其属性:

class SomeOtherObject
{
    readonly Dependent _dependent;
    public Dependent { get { return _dependent; }}

    public SomeOtherObject(Dependent dependent)
    {
        _dependent = dependent;
    }

    public void Print()
    {
        Console.WriteLine(_dependent.Main.Property);
    }
}

所以现在你可以这样做了:

var dependent = new Dependent(new Main { Property = 1 });
var someOtherObject = new SomeOtherObject(dependent);

// this will print "1"
someOtherObject.Print();

dependent.Main = new Main { Property = 5; };

// this will print "5"
someOtherObject.Print();

在这种情况下,显然,简单地更改dependent.Main.Property 也可以解决问题。因此,如果您的程序的所有部分都指向一个对象,您可以对其进行变异(即更改其内部数据)并且每个人都会看到更改,但您不能让程序的所有部分都更改它们所指向的内容。

值得注意的是,在多线程程序中执行此操作时需要小心;您很少希望其他线程能够随机更改您的内部数据。

这也是为什么最好尽量保持你的属性只读,如果可能的话,你的对象是不可变的。

【讨论】:

    【解决方案2】:

    做完之后

    var main = new Main { Property = 1 };
    

    你有一个Main 类型的对象分配在内存中的某个地方(我们将它命名为Main1),在某个内存地址X,变量main 指向那个对象。 “点”意味着它实际上存储了该对象的地址Main1,因此main 包含X

    然后将Main1的引用传递给Dependent对象的构造函数

    var dependent = new Dependent(main);
    

    Dependent 对象也分配在内存中的某处,其中一个字段存储对Main1 对象的引用。所以dependent.Main也存储了X。

    当你这样做时

    ChangeRef(ref main, new Main { Property = 5 });
    

    您在内存地址Y 的某处分配新对象Main5。现在您更改地址变量main 指向的地址。之前存储地址X(地址Main1),现在存储地址Y(地址Main5)。但是dependent.Main仍然存储着地址X,因为你没有做任何改变,所以它仍然指向对象Main1

    【讨论】:

    • 好的,但我想替换 main1,而不是引用,而是实际对象,以便所有指向该对象的引用都指向我的新对象。这可能吗?
    • @Erik83 不,这通常是不可能的。
    • 好的。很高兴知道,所以我可以停止追逐... =)但这看起来不奇怪吗?还是出于安全原因?
    • @Erik83 对我来说似乎并不奇怪,实际上不需要这样做。在您的示例中,您只需设置main.Property = 5,此更改将反映给使用此对象的每个人。你不需要ref
    • 好吧,如果您有一个包含大量嵌套对象的复杂对象,或者只是无法更改属性。我只是觉得奇怪的是不可能直接写入内存中的那个位置......
    猜你喜欢
    • 2014-07-19
    • 1970-01-01
    • 1970-01-01
    • 2015-10-08
    • 2022-11-15
    • 2018-06-02
    • 2012-12-05
    • 1970-01-01
    • 2012-08-14
    相关资源
    最近更新 更多