C#中引用类型的赋值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1065709/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:23:08  来源:igfitidea点击:

Value assignment for reference type in C#

c#.net

提问by James Jones

What is the proper way to implement assignment by value for a reference type? I want to perform an assignment, but not change the reference.

为引用类型实现按值赋值的正确方法是什么?我想执行任务,但不更改参考。

Here is what I'm talking about:

这就是我要说的:

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

Is there some sort of convention I should be using for this? I feel like I'm not the first person that has encountered this. Thanks.

我应该为此使用某种约定吗?我觉得我不是第一个遇到这种情况的人。谢谢。

EDIT:

编辑:

Wow. I think I need to tailor my question more toward the actual problem I'm facing. I'm getting a lot of answers that do not meet the requirement of not changing the reference. Cloning is not the issue here. The problem lies in ASSIGNING the clone.

哇。我想我需要根据我面临的实际问题调整我的问题。我得到了很多不符合不更改参考要求的答案。克隆不是这里的问题。问题在于分配克隆。

I have many classes that depend on A - they all share a reference to the same object of class A. So, whenever one classes changes A, it's reflected in the others, right? That's all fine and well until one of the classes tries to do this:

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

myA = new A();

In reality I'm not doing new A()but I'm actually retrieving a serialized version of A off the hard drive. But anyways, doing this causes myA to receive a NEW REFERENCE. It no longer shares the same A as the rest of the classes that depend on A. This is the problem that I am trying to address. I want all classes that have the instance of A to be affected by the line of code above.

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

I hope this clarifies my question. Thank you.

我希望这能澄清我的问题。谢谢你。

采纳答案by James Jones

I wish there was a "second best" answer option, because anyone who mentioned Observer deserves it. The observer pattern would work, however it is not necessary and in my opinion, is overkill.

我希望有一个“次佳”的答案选项,因为任何提到观察者的人都应得的。观察者模式可以工作,但是没有必要,而且在我看来,它是矫枉过正的。

If multiple objects need to maintain a reference to the same object ("MyClass", below) and you need to perform an assignmentto the referenced object ("MyClass"), the easiest way to handle it is to create a ValueAssign function as follows:

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

public class MyClass
{
    private int a;
    private int b;

    void ValueAssign(MyClass mo)
    {
        this.a = mo.a;
        this.b = mo.b;
    }
}

Observer would only be necessary if otheraction was required by the dependent objects at the time of assignment. If you wish to only maintain the reference, this method is adequate. This example here is the same as the example that I proposed in my question, but I feel that it better emphasizes my intent.

只有在分配时依赖对象需要其他操作时才需要观察者。如果您只想维护参考,此方法就足够了。这里的这个例子与我在问题中提出的例子相同,但我觉得它更好地强调了我的意图。

Thank you for all your answers. I seriously considered all of them.

感谢您的所有回答。我认真地考虑了所有这些。

回答by MiffTheFox

I believe you should be using a struct instead of a class than, as structs work by value and not by reference.

我相信您应该使用结构而不是类,因为结构是按值而不是按引用工作的。

回答by Jon Skeet

It sounds like you're talking about cloning. Some objects will support this (via ICloneable) but most won't. In many cases it doesn't make sense anyway - what does it mean to copy a FileStreamobject? ICloneableis generally regarded as a bad interface to use, partly because it doesn't specify the depth of the clone.

听起来你在谈论克隆。一些对象会支持这个(通过ICloneable),但大多数不会。在许多情况下,无论如何它都没有意义 - 复制FileStream对象意味着什么?ICloneable通常被认为是一个不好使用的接口,部分原因是它没有指定克隆的深度。

It's better to try to change your way of thinking so this isn't necessary. My guess is that you're a C++ programmer - and without wishing to cast any judgements at all: don't try to write C# as if it's C++. You'll end up with unidiomatic C# which may not work terrible well, may be inefficient, and may be unintuitive for C# developers to understand.

最好尝试改变您的思维方式,因此这不是必需的。我的猜测是,你是一个C ++程序员-并且不希望施放任何判断:不要试图写C#,如果它的C ++。你最终会得到单句的 C#,它可能工作得不好,可能效率低下,并且对于 C# 开发人员来说可能不直观。

One option is to try to make types immutable where possible - at that point it doesn't matterwhether or not there's a copy, as you wouldn't be able to change the object anyway. This is the approach that Stringtakes, and it works very well. It's just a shame that there aren't immutable collections in the framework (yet).

一种选择是在可能的情况下尝试使类型不可变 - 在这一点上,是否存在副本并不重要,因为无论如何您都无法更改对象。这是采用的方法String,并且效果很好。框架中还没有不可变的集合(还),这只是一种耻辱。

In your case, instead of having the ValueAssignmethod, you would have WithValuewhich would return a newinstance with just the value changed. (Admittedly that's the only value available in your case...) I realise that this sort of copying (of all but the property that's about to change) goes against what I was saying about copying being somewhat unidiomatic in C#, but it's within the class rather than an outside body deciding when to copy.

在您的情况下,ValueAssign您将拥有WithValuewhich 将返回一个仅更改值的实例,而不是使用该方法。(诚​​然,这是您的案例中唯一可用的价值......)我意识到这种复制(除了即将更改的属性之外的所有内容)与我所说的在 C# 中复制有点单调的说法背道而驰,但它在类而不是外部机构决定何时复制。

I suspect I'm not explaining this terribly well, but my general advice is to design around it rather than to try to explicitly copy all over the place.

我怀疑我没有很好地解释这一点,但我的一般建议是围绕它进行设计,而不是试图在整个地方明确复制。

回答by Shea

One approach is to use a copy constructor. e.g.,

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

MyClass orig = ...; MyClass copy = new MyClass(orig);

MyClass 原点 = ...; MyClass copy = new MyClass(orig);

Where you copy the elements of MyClass. Depending on how many reference types the class contains this might involve recursive use of copy constructors.

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

回答by Daniel Earwicker

Others have suggested cloning in their answer, but that's only part of the deal. You also want to use the results of a (possibly deep) clone to replace the contents of an existing object. That's a very C++-like requirement.

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

It just doesn't come up very often in C#, so there's no standard method name or operator meaning "replace the contents of this object with a copy of the contents of that object".

它只是在 C# 中不经常出现,所以没有标准的方法名称或运算符,意思是“用该对象内容的副本替换该对象的内容”。

The reason it occurs so often in C++ is because of the need to track ownership so that cleanup can be performed. If you have a member:

它在 C++ 中如此频繁出现的原因是因为需要跟踪所有权以便可以执行清理。如果您有会员:

std::vector<int> ints;

You have the advantage that it will be properly destroyed when the enclosing object is destroyed. But if you want to replace it with a new vector, you need swapto make that efficient. Alternatively you could have:

您的优势是,当封闭对象被销毁时,它将被正确销毁。但是如果你想用一个新的向量替换它,你需要swap让它变得高效。或者你可以有:

std::vector<int> *ints;

Now you can swap in a new one easily, but you have to remember to delete the old one first, and in the enclosing class's destructor.

现在你可以很容易地换入一个新的,但你必须记住先删除旧的,然后在封闭类的析构函数中。

In C# you don't need to worry about that. There's one right way:

在 C# 中,您无需担心。有一个正确的方法:

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

You don't have to clean it up, and you can swap in a new one by reference. Best of both.

您不必清理它,您可以通过引用换一个新的。两全其美。

Edit:

编辑:

If you have multiple "client" objects that need to hold a reference to an object and you want to be able to replace that object, you would make them hold a reference to an intermediate object that would act as a "wrapper".

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

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

The other classes would hold a reference to the Replaceable<T>. So would the code that needs to swap in a replacement. e.g.

其他类将持有对Replaceable<T>. 需要交换的代码也是如此。例如

Replaceable<FileStream> _fileStream;

It might also be useful to declare an event, so clients could subscribe to find out when the stored instance was replaced. Reusable version here.

声明一个事件也可能很有用,因此客户端可以订阅以了解何时替换了存储的实例。可重用版本在这里

You could also define implicit conversion operatorsto remove some syntax noise.

您还可以定义隐式转换运算符来消除一些语法噪音。

回答by Irwin

For what you want to do, I think A.ValueAssign(otherA) is the best way.

对于你想做的事情,我认为 A.ValueAssign(otherA) 是最好的方法。

Given that you want to have one reference of A around, ensuring that the reference isn't destroyed is key.

鉴于您希望有一个 A 的引用,确保引用不被破坏是关键。

Wouldn't you also be served by using a singleton pattern here as well?

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

回答by Groo

If I got it right, you are talking about proper Singleton deserialization.

如果我猜对了,您就是在谈论正确的单例反序列化。

  1. If you are using .Net native serialization then you might take a look at the MSDN ISerializableexample. The example shows exactly that - how to override ISerializable.GetObjectDatato return the same instance on each call.

  2. If you are using Xml serialization (XmlSerializer), then you manually implement IXmlSerializablein your object's parentclass, and then take care to get a single instance each time.

  3. A simplest way would be to ensure this in your parent property's setter, by accessing some kind of a static cache. (I find this pretty dirty, but that's an easy way to do it).

  1. 如果您使用 .Net 本机序列化,那么您可以查看 MSDNISerializable示例。该示例准确地显示了 - 如何覆盖ISerializable.GetObjectData以在每次调用时返回相同的实例。

  2. 如果您使用的是 Xml 序列化 ( XmlSerializer),则您IXmlSerializable在对象的类中手动实现,然后注意每次获取单个实例。

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

For example:

例如:

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

And then you would have a static class with some kind of a dictionary where you could quickly retrieve the singleton instance (or create it if it doesn't exist).

然后你会有一个带有某种字典的静态类,你可以在其中快速检索单例实例(或者如果它不存在就创建它)。

Although this may work for you, I also agree with the others that this is rarely the best (or only) way to do it. There is surely a way to refactor your code so that this becomes unnecessary, but you should provide some additional info about what is the intended usage, where is this data accessed from, or simply why do classes really need to reference a single object.

虽然这可能对你有用,但我也同意其他人的看法,这很少是最好的(或唯一的)方法。肯定有一种方法可以重构您的代码,从而使这变得不必要,但是您应该提供一些关于预期用途的附加信息,从哪里访问这些数据,或者只是为什么类真的需要引用单个对象。

[Edit]

[编辑]

Since you areusing a static cache similar to a singleton, you should take care to implement it properly. That means several things:

既然你正在使用类似单静态缓存,你应该照顾来实现它正确。这意味着几件事:

  1. Your cache class should have a privateconstructor. You don't want anyone to create a new instance explicitly - this is not an option when using singletons. So this means you should expose some public static property like Cache.Instancewhich will alwaysreturn the reference to the same private object. Since the constructor is private, you are sure that only Cache class can create the instance during initialization (or first update). Details on implementing this pattern can be found at http://www.yoda.arachsys.com/csharp/singleton.html(which is also a great thread-safe implementation).

  2. When all you object have the same instance, then you can simply notify cache to update the single private instance (e.g. call Cache.Update()from somewhere). This way you can update the only instance which everyone is using.

  1. 您的缓存类应该有一个私有构造函数。您不希望任何人显式地创建新实例——这在使用单例时不是一个选项。所以这意味着你应该公开一些公共静态属性,比如Cache.Instance总是返回对同一个私有对象的引用。由于构造函数是私有的,您可以确定只有 Cache 类可以在初始化(或第一次更新)期间创建实例。有关实现此模式的详细信息,请访问 http://www.yoda.arachsys.com/csharp/singleton.html(这也是一个很好的线程安全实现)。

  2. 当所有对象都具有相同的实例时,您可以简单地通知缓存更新单个私有实例(例如Cache.Update()从某处调用)。这样您就可以更新每个人都在使用的唯一实例。

But it still not clear from your example how exactly you are notifying your clients that the data has been updated anyway. An event-driven mechanism would work better, as it would allow you to decouple your code - Singletons are evil.

但是从您的示例中仍然不清楚您究竟是如何通知您的客户数据已经更新了。事件驱动机制会更好地工作,因为它可以让你解耦你的代码——单例是邪恶的

回答by Joe Erickson

We have cases where we do exactly what you are talking about. We have many objects referencing a particular instance of an object and we want to change the instance of the object so that every object referencing that existing instance see the change.

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

The pattern we follow is almost what you have - just the names are different:

我们遵循的模式几乎就是你所拥有的——只是名称不同:

    class A
    {
        int i;
        public A(int i)
        {
            this.i = i;
        }
        public void Copy(A source)
        {
            this.i = source.i;
        }
    }

回答by Rune FS

It's not possible to overload the assignment operator in c# as it would be in c/c++. However even if that was an option I'd say your trying to fix the symptom not the problem. Your problem is that the assignment of a new reference is breaking code, why not just assign the read values to the original reference? and If you are affraid that others might new and assign make the object into a singleton or similar so that it can't be altered after creation but the reference will stay the same

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

回答by Bevan

In several WinForms based applications, I've needed similar functionality, in my case to allow a data entry form to work on a copy of the object, information from which is copied onto the original object only if the user elects to save the changes.

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

To make this work, I brought across an idea from my Delphi days - the Assign()method.

为了完成这项工作,我从我的 Delphi 时代提出了一个想法 -Assign()方法。

Essentially, I wrote (well, ok, generated) a method that copies across properties (and list contents, etc etc) from one instance to another. This allowed me to write code like this:

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

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

Changes made within the dialog are private until the user chooses to save them, then the public variable (person) is updated.

在对话框中所做的更改是私有的,直到用户选择保存它们,然后person更新公共变量 ( )。

An Assign()method for Personmight look like this:

Assign()方法Person可能如下所示:

public void Assign(Person source)
{
    Name = source.Name;
    Gender = source.Gender;
    Spouse = source.Spouse;

    Children.Clear();
    Children.AddRange( source.Children);
}

As an aside, having an Assign()method makes a copy-constructor almost trivially easy to write:

Assign()顺便说一句,拥有一个方法可以使复制构造函数几乎非常容易编写:

public Person(Person original)
    : this()
{
    Assign(original);
}