简体   繁体   English

struct c# 内部数据结构(引用类型)的值行为

[英]Value behavior of data structure (reference type) inside struct c#

As far as I got to understand Structs are value types which means they are copied when they are passed around.据我了解,结构是值类型,这意味着它们在传递时会被复制。 So if you change a copy you are changing only that copy, not the original and not any other copies which might be around.因此,如果您更改副本,您只会更改该副本,而不是原始副本,也不会更改可能存在的任何其他副本。

However, you might have a struct with a list of classes, of which you want value behaviour, for example:但是,您可能有一个包含类列表的结构,您希望这些类的值行为,例如:

private struct InitData {
    public string name;
    public float initialSpeed;
    public bool loop;
    public List<Settings> settings;
}

Settings is a class with a lot of simple types , that is a class due to need of reference behaviolur.设置是一个 class 有很多简单的类型,即一个 class 由于需要参考行为。

For my case I need to keep the initial value of a class, for which I need this struct to keep the data.对于我的情况,我需要保留 class 的初始值,为此我需要此结构来保留数据。

Simple types behave as expected, and they are not modified when I set the values from this struct, not so with the List<Settings> Settings that changes values, because behaves as a reference type although it is inside a struct.简单类型的行为符合预期,并且当我从该结构设置值时它们不会被修改,而不是更改值的List<Settings> Settings ,因为尽管它在结构内部但表现为引用类型。

So I have to do to my _initData struct instance and to my List<Settings> _settings instance to break with the reference type behaviour of the list.所以我必须对我的_initData结构实例和我的List<Settings> _settings实例做一些事情来打破列表的引用类型行为。

_settings = _initData.settings;
//renew instace to have value behaviour:
_initData.nodeSettings = new List<Settings>();
_initData.nodeSettings = (List<Settings>)DeepCopy(_settings.ToList());

So, as it does not seem very appropiate to generate a new instance of the list every single time, is there any way to achieve value behaviour inside a struct for a data structure to handle many instances or references with value type behaviour (meaning list, array, vector, etc.)?因此,由于每次生成列表的新实例似乎不太合适,有没有办法在结构内部实现值行为,以便数据结构处理具有值类型行为的许多实例或引用(意思是列表,数组、向量等)?

As mentioned by @InBetween, if a struct containing a reference is copied, only the reference is copied, not the object the reference refers to.正如@InBetween 所述,如果复制包含引用的结构,则仅复制引用,而不复制引用所指的 object。

Mutable structs are widely considered evil , so it is a good idea to avoid them in general.可变结构被广泛认为是邪恶的,因此通常避免使用它们是个好主意。 If a immutable struct contains a reference to a mutable object it will behave just like any other reference that is passed around, ie when you mutate the object you must consider if the object is shared, and if so, if you want to mutate the object, or mutate a copy. If a immutable struct contains a reference to a mutable object it will behave just like any other reference that is passed around, ie when you mutate the object you must consider if the object is shared, and if so, if you want to mutate the object ,或变异副本。

A nice way to avoid any problems with this is ensure the objects are immutable all the way down, so that any change requires creating a copy.避免任何问题的一个好方法是确保对象自始至终都是不可变的,因此任何更改都需要创建一个副本。 Example using System.Collections.Immutable library:使用 System.Collections.Immutable 库的示例:

public class Settings
{
    //Should only contain immutable fields
    public int MyValue { get; }

    public Settings(int myValue) => MyValue = myValue;

    // Can contain helpers to create a mutated object
    public Settings WithMyValue(int newMyValue) => new Settings(newMyValue);
}
public struct InitData
{
    public string Name { get; }
    public float InitialSpeed { get; }
    public bool Loop { get; }
    public ImmutableList<Settings> Settings { get; }

    public InitData(string name, float initialSpeed, bool loop, ImmutableList<Settings> settings)
    {
        this.Name = name;
        this.InitialSpeed = initialSpeed;
        this.Loop = loop;
        this.Settings = settings;
    }

    public InitData(string name, float initialSpeed, bool loop, IEnumerable<Settings> settings)
        : this(name, initialSpeed, loop, settings.ToImmutableList())
    { }

    public InitData MutateSettings(Func<ImmutableList<Settings>, ImmutableList<Settings>> mutator) 
        => new InitData(Name, InitialSpeed, Loop, mutator(Settings));
}

Another alternative, as you mention, is to create deep copies, example:正如您所提到的,另一种选择是创建深层副本,例如:

public class Settings
{
    public int MyValue { get; set; }
    public Settings(int myValue) => MyValue = myValue;
    public Settings Clone() => new Settings(MyValue);
}
public struct InitData
{
    public string Name { get; }
    public float InitialSpeed { get; }
    public bool Loop { get; }
    public List<Settings> Settings { get; }

    public InitData(string name, float initialSpeed, bool loop, List<Settings> settings)
    {
        this.Name = name;
        this.InitialSpeed = initialSpeed;
        this.Loop = loop;
        Settings = settings;
    }

    public InitData Clone() => new InitData(Name, InitialSpeed, Loop, Settings.Select(s => s.Clone()).ToList());
}

Since InitData is shallow-immutable it does not matter if it is a value or reference type, but since it is not deep-immutable you need to clone it whenever you want a unchanging copy.由于 InitData 是浅不可变的,因此它是值类型还是引用类型并不重要,但由于它不是深不可变的,因此您需要在需要不变副本时克隆它。

It can be rather difficult to create a method that transparently creates deep copies.创建一种透明地创建深层副本的方法可能相当困难。 The runtime does not keep know if objects are deep-immutable or not, so it would need to copy all fields, and there is a risk you might copy much more than you intended.运行时不知道对象是否是深度不可变的,因此它需要复制所有字段,并且存在复制的风险可能比预期的多得多。 You would also need some way to handle circular references.您还需要一些方法来处理循环引用。 So I'm not aware of any c-style language that does has this.所以我不知道有任何 c 风格的语言有这个。

There is a method that uses serialization, like BinaryFormatter , for deep copies, but I would not recommend it.有一种使用序列化的方法,比如BinaryFormatter ,用于深拷贝,但我不推荐它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM