簡體   English   中英

struct c# 內部數據結構(引用類型)的值行為

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

據我了解,結構是值類型,這意味着它們在傳遞時會被復制。 因此,如果您更改副本,您只會更改該副本,而不是原始副本,也不會更改可能存在的任何其他副本。

但是,您可能有一個包含類列表的結構,您希望這些類的值行為,例如:

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

設置是一個 class 有很多簡單的類型,即一個 class 由於需要參考行為。

對於我的情況,我需要保留 class 的初始值,為此我需要此結構來保留數據。

簡單類型的行為符合預期,並且當我從該結構設置值時它們不會被修改,而不是更改值的List<Settings> Settings ,因為盡管它在結構內部但表現為引用類型。

所以我必須對我的_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());

因此,由於每次生成列表的新實例似乎不太合適,有沒有辦法在結構內部實現值行為,以便數據結構處理具有值類型行為的許多實例或引用(意思是列表,數組、向量等)?

正如@InBetween 所述,如果復制包含引用的結構,則僅復制引用,而不復制引用所指的 object。

可變結構被廣泛認為是邪惡的,因此通常避免使用它們是個好主意。 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 ,或變異副本。

避免任何問題的一個好方法是確保對象自始至終都是不可變的,因此任何更改都需要創建一個副本。 使用 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));
}

正如您所提到的,另一種選擇是創建深層副本,例如:

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

由於 InitData 是淺不可變的,因此它是值類型還是引用類型並不重要,但由於它不是深不可變的,因此您需要在需要不變副本時克隆它。

創建一種透明地創建深層副本的方法可能相當困難。 運行時不知道對象是否是深度不可變的,因此它需要復制所有字段,並且存在復制的風險可能比預期的多得多。 您還需要一些方法來處理循環引用。 所以我不知道有任何 c 風格的語言有這個。

有一種使用序列化的方法,比如BinaryFormatter ,用於深拷貝,但我不推薦它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM