3

我正在尝试创建一个永远不会改变的常量值数组。我尝试这样的事情......

public class ColItem
{
    public string Header { get; set; }              // real header name in ENG/CHI
    public string HeaderKey { get; set; }           // header identifier
    public bool IsFrozen { get; set; } = false;
    public bool IsVisible { get; set; } = true;
    public int DisplayIdx { get; set; }
}

public struct DatagridSettingDefault
{
    public static ImmutableArray<ColItem> DataGrid_MarketPriceTab = ImmutableArray.Create(new ColItem[] {
        new ColItem { HeaderKey = "ORDER_ID", IsFrozen = true, DisplayIdx = 0 },
        new ColItem { HeaderKey = "PRICE_RENAMEPROMPT", IsFrozen = false, DisplayIdx = 1 },
        new ColItem { HeaderKey = "PRICETBL_TRADESTATENO", IsFrozen = false, DisplayIdx = 2 },
        new ColItem { HeaderKey = "PRICETBL_BIDQTY", DisplayIdx = 3 },
        new ColItem { HeaderKey = "PRICETBL_BID", DisplayIdx = 4 },
        new ColItem { HeaderKey = "PRICETBL_ASK", DisplayIdx = 5 },
        new ColItem { HeaderKey = "PRICETBL_ASKQTY", DisplayIdx = 6 }
}

但是,这个数组仍然从代码的某个地方(另一个线程)更改。如何使数组保持不变并且在程序的整个生命周期中绝对不能改变?我只需要在编译时初始化它,无论什么引用或其他线程改变它,它都不应该改变。但我的情况,它一直在变化。

例如,如果我稍后在代码中或通过另一个线程执行类似的操作,它会发生变化......

HeaderKey = col.Header.ToString(),
Header = "whatever"
IsFrozen = col.IsFrozen,
IsVisible = true,
DisplayIdx = col.DisplayIndex

我如何解决它?

4

4 回答 4

1

你不需要ImmutableArray<T>。只需这样做:

  • DataGrid_MarketPriceTab从可变字段更改为仅 getter 属性。
  • 我建议使用IReadOnlyList<T>,而不是ImmutableArray<T>因为它内置在 .NET 的 BCL 中,同时ImmutableArray<T>添加了对System.Collections.Immutable.
  • 请注意,该属性被内联初始化为Array( ColItem[]),但是因为对它的唯一引用是通过,因此IReadOnlyList<T>如果不使用反射,就无法更改集合。
    • 使其成为{ get; }-only 属性意味着您的类的消费者不能重新分配属性值。
    • 如果您确实需要使用字段(而不是属性),则将其设为static readonly字段。

您还需要使ColItem不可变 - 这可以通过使所有成员属性{ get; }而不是{ get; set; }(并确保它们的类型也是不可变的)来完成:

像这样:

// This is an immutable type: properties can only be set in the constructor.
public class ColItem
{
    public ColItem(
        string headerName, string headerKey, int displayIdx,
        bool isFrozen = false, bool isVisible = true
    )
    {
        this.Header     = headerName ?? throw new ArgumentNullException(nameof(headerName));
        this.HeaderKey  = headerKey ?? throw new ArgumentNullException(nameof(headerKey));
        this.IsFrozen   = isFrozen;
        this.IsVisible  = isVisible;
        this.DisplayIdx = displayIdx;
    }

    public string Header     { get; }
    public string HeaderKey  { get; }
    public bool   IsFrozen   { get; }
    public bool   IsVisible  { get; }
    public int    DisplayIdx { get; }
}

public struct DatagridSettingDefault // Why is this a struct and not a static class?
{
    public static IReadOnlyList<ColItem> DataGrid_MarketPriceTab { get; } = new[]
    {
        new ColItem( headerName: "Order Id", headerKey: "ORDER_ID", isFrozen: true, displayIdx: 0 ),
        new ColItem( headerName: "Price", headerKey: "PRICE_RENAMEPROMPT", IsFrozen:false, displayIdx:1 ),
        new ColItem( headerName: "Foo", headerKey: "PRICETBL_TRADESTATENO", IsFrozen:false, displayIdx:2 ),
        new ColItem( headerName: "Bar", headerKey: "PRICETBL_BIDQTY", displayIdx:3 ),
        new ColItem( headerName: "Baz", headerKey: "PRICETBL_BID", displayIdx:4 ),
        new ColItem( headerName: "Qux", headerKey: "PRICETBL_ASK", displayIdx:5 ),
        new ColItem( headerName: "Hmmm", headerKey: "PRICETBL_ASKQTY", displayIdx:6 )
    };
}

如果所有这些对您来说似乎很乏味,我同意!好消息是,如果您使用 C# 9.0 或更高版本(不幸的是,它需要 .NET 5),您可以使用 Immutable Record Types,因此class ColItem该类变为record ColItem

public record ColItem(
    string Header,
    string HeaderKey,
    bool   IsFrozen = false,
    bool   IsVisible = true,
    int    DisplayIdx
);
于 2021-01-11T04:21:31.853 回答
0

让你的ColItem类不可变。有几种方法可以做到这一点,但最直接的方法是set从属性中删除关键字,使它们readonly

于 2021-01-11T04:24:32.690 回答
0

如果要创建不可变引用类型,可以将所有属性设为私有设置器。或者在 C# 9 中,您可以使用仅 init 属性,这些属性可以使用对象初始值设定项来构造

public string Header { get; init; } 

或者您可以更进一步,创建一个Record具有其他几个优点的,例如With-expressions基于值的相等性以及与具有继承的结构不同

public record ColItem
{
   public string Header { get; init; } // real header name in ENG/CHI
   public string HeaderKey { get; init; } // header identifier
   public bool IsFrozen { get; init; } = false;
   public bool IsVisible { get; init; } = true;
   public int DisplayIdx { get; init; }
}

用法

 var test = new DatagridSettingDefault();
 test.DataGrid_MarketPriceTab[0].HeaderKey = "asd";

将产生以下编译器错误

CS8852 仅初始化属性或索引器“ColItem.HeaderKey”只能在对象初始化程序中分配,或者在实例构造函数或“init”访问器中的“this”或“base”上分配。

那么剩下的就是选择一个不可变的集合/集合接口

于 2021-01-11T04:33:55.670 回答
0

这是使ColItem类不可变的方法:

public class ColItem
{
    public string HeaderKey { get; }
    public bool IsFrozen { get; }

    public ColItem(string headerKey, bool isFrozen = false)
    {
        HeaderKey = headerKey;
        IsFrozen = isFrozen;
    }
}

通过只声明get访问器,除了类型的构造函数之外,属性在任何地方都变得不可变。(文档

在 C# 9 中,还提供了{ get; init; }属性选项,这些属性在初始化期间可变,之后变为不可变,以及record默认不可变的新类型。

以下是如何使静态属性DatagridSettingDefault.DataGrid_MarketPriceTab不可变,方法是将其标记为readonly

public class DatagridSettingDefault
{
    public static readonly ImmutableArray<ColItem> DataGrid_MarketPriceTab
        = ImmutableArray.Create(new ColItem[]
    {
        new ColItem ("ORDER_ID", true),
        new ColItem ("PRICE_RENAMEPROMPT", false),
        new ColItem ("PRICETBL_TRADESTATENO", false),
        new ColItem ("PRICETBL_BIDQTY"),
        new ColItem ("PRICETBL_BID"),
        new ColItem ("PRICETBL_ASK"),
        new ColItem ("PRICETBL_ASKQTY"),
    });
}
于 2021-01-11T04:35:41.680 回答