代码之家  ›  专栏  ›  技术社区  ›  James Jones

C中引用类型的赋值#

  •  10
  • James Jones  · 技术社区  · 17 年前

    为引用类型实现按值赋值的正确方法是什么?我想执行一个作业,但不想更改引用。

    以下是我正在谈论的内容:

    void Main()
    {
        A a1 = new A(1);
        A a2 = new A(2);
        a1 = a2; //WRONG: Changes reference
        a1.ValueAssign(a2); //This works, but is it the best way?
    }
    
    class A
    {
        int i;
    
        public A(int i)
        {
            this.i = i;
        }
    
        public void ValueAssign(A a)
        {
            this.i = a.i;
        }
    }
    

    对此,我应该使用某种惯例吗?我觉得我不是第一个遇到这种情况的人。谢谢。

    编辑:

    我有很多依赖于A的类,它们都共享对类A的同一个对象的引用。所以,每当一个类更改A时,它都会反映在其他类中,对吗?这一切都很好,直到其中一个班级尝试这样做:

    myA = new A();
    

    事实上,我没有做 new A() 但我实际上正在从硬盘中检索a的序列化版本。但无论如何,这样做会使myA收到一个新的参考。它不再与依赖于A的其他类共享相同的A。这就是我试图解决的问题。我希望所有具有A实例的类都受到上述代码行的影响。

    12 回复  |  直到 17 年前
        1
  •  6
  •   Jon Skeet    17 年前

    听起来你在谈论克隆。一些对象将支持此功能(通过 ICloneable FileStream 对象? 通常被认为是一个糟糕的接口,部分原因是它没有指定克隆的深度。

    根本

    无论是否有副本,因为无论如何都无法更改对象。这是一种方法 String 需要,而且效果很好。令人遗憾的是,框架中还没有不可变的集合。

    ValueAssign 方法,你会有 WithValue 这将返回一个 仅更改了值的实例。(不可否认,这是你案例中唯一可用的值……)我意识到,这种复制(除了即将更改的属性之外的所有属性)与我所说的在C#中复制有点不符合逻辑,但它是在类内而不是由外部机构决定何时复制的。

        2
  •  2
  •   MiffTheFox    17 年前

        3
  •  2
  •   Irwin    17 年前

    考虑到你想有一个A的引用,确保引用不被破坏是关键。

    在这里使用单例模式难道不会对你也有好处吗?

        4
  •  1
  •   Shea    17 年前

    一种方法是使用复制构造函数。例如。,

    MyClass orig=。..;

    在那里复制MyClass的元素。根据类包含的引用类型的数量,这可能涉及复制构造函数的递归使用。

        5
  •  1
  •   Joe Erickson    17 年前

    我们有一些案例,我们完全按照你说的去做。我们有许多对象引用一个对象的特定实例,我们想更改该对象的实例,以便引用该现有实例的每个对象都能看到更改。

        class A
        {
            int i;
            public A(int i)
            {
                this.i = i;
            }
            public void Copy(A source)
            {
                this.i = source.i;
            }
        }
    
        6
  •  1
  •   Daniel Earwicker    17 年前

    其他人在回答中建议克隆,但这只是交易的一部分。您还希望使用(可能是深度)克隆的结果来替换现有对象的内容。这是一个非常类似C++的要求。

    它在C++中经常发生的原因是需要跟踪所有权,以便进行清理。如果您有会员:

    std::vector<int> ints;
    

    您的优点是,当封闭对象被销毁时,它将被正确销毁。但如果你想用一个新的向量替换它,你需要 swap

    std::vector<int> *ints;
    

    在C#中,你无需为此担忧。有一个正确的方法:

    List<int> ints = new List<int>();
    

    你不必清理它,你可以通过引用换一个新的。两者中最好的。

    编辑:

    如果您有多个“客户端”对象需要保存对某个对象的引用,并且您希望能够替换该对象,则可以使它们保存对充当“包装器”的中间对象的引用。

    class Replaceable<T>
    {
        public T Instance { get; set; }
    }
    

    Replaceable<T>

    Replaceable<FileStream> _fileStream;
    

    Reusable version here .

    implicit conversion operators

        7
  •  1
  •   Bevan    17 年前

    在几个基于WinForms的应用程序中,我需要类似的功能,在我的例子中,允许数据输入表单在对象的副本上工作,只有当用户选择保存更改时,信息才会从该副本复制到原始对象上。

    为了实现这一目标,我从德尔菲时代学到了一个想法—— Assign() 方法。

    本质上,我编写了(好吧,生成了)一个方法,该方法将属性(和列表内容等)从一个实例复制到另一个实例。这让我可以写这样的代码:

    var person = PersonRespository.FindByName("Bevan");
    ...
    var copy = new Person();
    copy.Assign(person);
    using (var form = new PersonDataEntryForm(copy))
    {
        if (form.ShowAsModelessDialog() == MessageReturn.Save)
        {
            person.Assign(copy);
        }
    }
    

    person

    分配() 方法 Person 可能看起来像这样:

    public void Assign(Person source)
    {
        Name = source.Name;
        Gender = source.Gender;
        Spouse = source.Spouse;
    
        Children.Clear();
        Children.AddRange( source.Children);
    }
    

    顺便说一句,有一个 分配()

    public Person(Person original)
        : this()
    {
        Assign(original);
    }
    
        8
  •  1
  •   James Jones    16 年前

    如果多个对象需要维护对同一对象(下面的“MyClass”)的引用,并且您需要执行 对于引用的对象(“MyClass”),处理它的最简单方法是创建一个ValueAssign函数,如下所示:

    public class MyClass
    {
        private int a;
        private int b;
    
        void ValueAssign(MyClass mo)
        {
            this.a = mo.a;
            this.b = mo.b;
        }
    }
    

    只有在以下情况下才需要观察员 其他 在分配时,依赖对象需要执行操作。如果你只想保留引用,这种方法就足够了。这里的例子与我在问题中提出的例子相同,但我觉得它更好地强调了我的意图。

        9
  •  1
  •   EpiX    15 年前

    我也遇到了同样的问题。 我解决这个问题的方法是将所有事物都引用的对象放在另一个对象内,并让所有事物引用外部对象。然后,您可以更改内部对象,所有内容都可以引用新的内部对象。

    OuterObject.InerObject.stuff
    
        10
  •  0
  •   Rune FS    17 年前

    在c#中不能像在c/c++中那样重载赋值运算符。然而,即使这是一种选择,我也会说你试图解决症状而不是问题。你的问题是,分配新的引用会破坏代码,为什么不直接将读取的值分配给原始引用呢?如果你担心其他人可能会新建和分配对象,请将对象设置为单例或类似对象,以便在创建后无法更改,但引用将保持不变

        11
  •  0
  •   vgru    17 年前

    1. 如果你正在使用。Net原生序列化,然后您可以查看MSDN ISerializable 例子。这个例子正好说明了这一点——如何覆盖 ISerializable.GetObjectData 在每次调用时返回相同的实例。

    2. XmlSerializer IXmlSerializable 在你的对象中 类,然后每次都注意获取一个实例。

    3. 最简单的方法是通过访问某种静态缓存,在父属性的setter中确保这一点。(我觉得这很脏,但这是一种简单的方法)。

    例如:

     public class ParentClass
     {
          private ReferencedClass _reference;
          public ReferencedClass Reference
          {
              get
              { 
                  return _reference;
              }
              set
              {
                  // don't assign the value, but consult the
                  // static dictionary to see if we already have
                  // the singleton
                  _reference = StaticCache.GetSingleton(value);
              }
          }
     }
    

    然后,您将拥有一个带有某种字典的静态类,您可以在其中快速检索单例实例(或者在不存在的情况下创建它)。

    虽然这可能对你有效,但我也同意其他人的观点,即这很少是最好的(或唯一的)方法。当然有一种方法可以重构你的代码,这样就没有必要了,但你应该提供一些额外的信息,说明预期用途是什么,从哪里访问这些数据,或者只是为什么类真的需要引用一个对象。

    [编辑]

    既然你 适当地

    1. 你的缓存类应该有一个 Cache.Instance 这将 总是 返回对同一私有对象的引用。 由于构造函数是私有的,因此可以确定只有Cache类可以在初始化(或第一次更新)期间创建实例。 有关实现此模式的详细信息,请参阅 http://www.yoda.arachsys.com/csharp/singleton.html

    2. Cache.Update()

    但从你的例子中仍然不清楚你到底是如何通知客户数据已经更新的。事件驱动机制会更好地工作,因为它允许您解耦代码- Singletons are evil .

        12
  •  0
  •   StriplingWarrior    17 年前

    public class B
    {
        B(int i) {A = new A(i);}
        public A A {get; set;}
    }
    
    ...
    
    void Main()
    {
        B b1 = new B(1);
        A a2 = new A(2);
        b1.A = a2;
    }