在 C# 中对字典进行切片

Slicing a Dictionary in C#

我正在尝试在 C# 中创建一个 class,它具有从 JSON 文件获取的字典 (bigDict)。 bigDict 将字符串映射到大数据结构。由此,我想创建另一个字典 (smallDict),它将键中的每个字符串映射到 bigDict 的数据结构值中的一个元素(字符串)。

我试过使用 get 和 set 来创建 smallDict。我能够成功使用 get,但我卡在了 set

class myClass
{
    public Dictionary<string, SomeStruct> bigDict{ get; set; } /*get and set from a JSON file*/

    public virtual Dictionary<string, string> smallDict
    {
        get => bigDict.ToDictionary(x => x.Key, x => x.Value.ElemFromStruct);   //works fine

        set => smallDict.ToDictionary.update(key , value); //doesn't work fine
    }
}

由于无法正确设置,我在一些预先存在的测试用例中遇到 property or indexer cannot be assigned to, is readonly 错误(我正在重构代码)

如果我没理解错的话,您希望能够从 Dictionary<string, string> 创建 Dictionary<string, SomeStruct>。为此,您需要从字符串值创建结构的实例。可以做到,但这意味着 SomeStruct 值将仅在 set.

期间分配单个 属性 (ElemFromStruct)

请注意,对于任何 属性,在 set 方法内部,有一个可用的 value 变量,它与 属性 的类型相同(Dictionary<string, string> 在这种情况下),它表示正在传递到 set 方法的项目。这在下面用作 value.ToDictionary(...

例如:

public virtual Dictionary<string, string> SmallDict
{
    get
    {
        return BigDict.ToDictionary(x => x.Key, x => x.Value.ElemFromStruct);
    }
    set
    {
        BigDict = value.ToDictionary(x => x.Key,
            x => new SomeStruct {ElemFromStruct = x.Value});
    }
}

但是,如果目的只是提供一种从 BigDict 获取 SmallDict 的方法(而不是反过来),那么您只需删除 set 方法,因为允许用户仅基于单个字符串为每个 SomeStruct 值设置 BigDict 可能没有多大意义:

public virtual Dictionary<string, string> SmallDict
{
    get
    {
        return BigDict.ToDictionary(x => x.Key, x => x.Value.ElemFromStruct);
    }
}

定义一个"one-off"类型SmallDict:

    public SmallDict<string> smallDict;

    public myClass()
    {
        smallDict = new SmallDict<string>(bigDict);
    }
    class SmallDict<TKey>
    {
        public readonly Dictionary<TKey, SomeStruct> BigDict;

        public SmallDict(Dictionary<TKey, SomeStruct> bigDict)
        {
            BigDict = bigDict;
        }

        public string this[TKey key]
        {
            get => BigDict[key].ElemFromStruct;
            set {
                var obj = BigDict[key];
                obj.ElemFromStruct = value;
                BigDict[key] = obj;
            }
        }
    }

使用方法如下:

Console.WriteLine(smallDict["key1"]); // Equivalent to printing bigDict[key].ElemFromStruct
smallDict["key1"] = "new value";      // Equivalent to bigDict[key].ElemFromStruct = "new value"

它只是字典的包装器,因此如果您需要比普通索引更多的方法,则必须手动完成所有管道。

通用性

请注意 SmallDict 如何仅适用于 SomeStruct...

的词典

我会写一个通用的 DictionarySlice class,但是任何实际通用性的尝试都会被 C# 严格的类型系统所挫败:没有令人满意的方式来通用地告诉属性 切片。

可能的解决方案:

  • getterssetters 作为 lambda 传递——当然你可以这样做,你会有更多的代码请注意,您的 lambda 表达式比实际 class 中的要大。如果您需要对许多不同的属性进行切片,它可能会很有用。
  • 使 SomeStruct 实现通用 class 将使用的 IGetSetElemFromStruct -- 不是非常优雅,如果您有许多属性要分割的话,可能会非常麻烦。
  • 反思 -- 为了完整性而包括在内,但不会对其进行扩展...

大型结构

避免大结构;更重要的是 避免可变结构 ,它被普遍认为是 Very Bad Thing(TM) in C#. From Microsoft's design guidelines,强调我的:

In general, structs can be very useful but should only be used for small, single, immutable values that will not be boxed frequently.

set {
    var obj = BigDict[key];
    obj.ElemFromStruct = value;
    BigDict[key] = obj;
}

请注意,在上面的 setter 中,我获取了结构的副本,对其进行修改,然后将其复制回字典中……不太好。如果它会很大,帮自己一个忙 使用 class。然后你只需写:

set { BigDict[key].ElemFromStruct = value; }

属性中的重要操作 get/set

这是关于您作为示例编写的代码:

public virtual Dictionary<string, string> smallDict
{
    get => bigDict.ToDictionary(x => x.Key, x => x.Value.ElemFromStruct);   //works fine

尽可能避开它们。没有明确说明属性 getters/setters 中可以包含哪些内容,但是在 get 中创建和填充字典以任何合理的标准来说都太过分了:想象一下,如果您不得不怀疑 每个 属性 访问。

也许根本不用这个

这个解决方案是我能想到的最好的解决方案,但它很笨重而且不是很有用。你必须根据你的用例自己判断,但通常 accessing/mutating bigDict[key].ElemFromStruct 直接 .

会更好