【问题标题】:Slicing a Dictionary in C#在 C# 中对字典进行切片
【发布时间】:2019-08-21 21:05:17
【问题描述】:

我正在尝试在 C# 中创建一个具有从 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 错误(我正在重构代码)

【问题讨论】:

  • ToDictionary.update() 应该是什么意思?它告诉我 ToDictionary 是一种方法,在给定的上下文中无效。另外......在我看来,getter 似乎返回了一个临时对象。您的 setter 调用 getter,获取临时对象,然后对该临时对象执行一些操作。为什么?此外,key 未定义。
  • @EdPlunkett 是的,可能不是。我对 C# 很陌生,所以我只是在尝试一些东西。可能我写 set 的方式是完全错误的
  • 我想,为了可读性、可测试性和可维护性,您不应该这样做。创建单独的类,将所有逻辑封装在类中。

标签: c# .net dictionary getter-setter


【解决方案1】:

定义一个“一次性”类型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 类,但是任何实际通用性的尝试都会受到 C# 严格的类型系统的阻碍:没有令人满意的方法来通用地判断要对哪个属性进行切片。

可能的解决方案:

  • getterssetters 作为 lambdas 传递 - 请注意,您可以做到这一点,您的 lambdas 中的代码将比实际课程中的代码多,请注意。如果您需要对许多不同的属性进行切片,它可能会很有用。
  • 使SomeStruct 实现泛型类将使用的IGetSetElemFromStruct - 如果您有许多属性需要切片,则不会非常优雅并且可能非常麻烦。
  • 反射——为了完整性而包含在内,但不会对其进行扩展...

大型结构

避免大型结构;更重要的是避免使用可变结构,它们在 C# 中被普遍认为是 Very Bad Thing(TM)来自Microsoft's design guidelines,强调我的:

一般来说,结构体非常有用,但只能用于small、单一、不可变值,不会经常装箱。

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

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

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

属性获取/设置中的重要操作

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

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

尽可能避免使用它们。没有明确的规定可以在属性 getter/setter 中使用什么,但在 get 中创建和填充字典对于任何合理的标准来说都太过分了:想象一下,如果你不得不怀疑 每个属性访问。

也许根本不使用这个

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

【讨论】:

  • 非常感谢@hugo 的出色回答!这对我有用。你是对的,我可能不应该这样做,但是重构所有内容以使用 bigDict 太过分了,所以我必须做切片的事情
  • 只要你知道你在做什么 :) 你的问题突出了 C# 类型系统的一个“缺点”——这在 Javascript 中本来是一件容易的事!
【解决方案2】:

如果我对您的理解正确,您希望能够从 Dictionary&lt;string, string&gt; 创建一个 Dictionary&lt;string, SomeStruct&gt;。为此,您需要从字符串值创建结构的实例。可以这样做,但这意味着 SomeStruct 值将只有在 set 期间分配的单个属性 (ElemFromStruct)。

请注意,对于任何属性,在 set 方法中,有一个可用的 value 变量与属性(在本例中为 Dictionary&lt;string, string&gt;)的类型相同,它表示要传递到的项目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);
    }
}

【讨论】:

  • 非常感谢@Rufus L。这确实解决了我的问题。但你是对的,我现在宁愿允许从smallDict 修改bigDict。有什么方法可以真正从bigDict 创建切片字典?而不是从bigDict 获取/设置的临时类型。我尝试在构造函数中执行此操作,但这不起作用,因为 JSON {get; set;} 在创建 smallDict 之前未实现导致空的 smallDict 再次感谢!
  • 我不知道你说的 “有什么方法可以真正从bigDict 创建切片字典吗?” 上面的代码有什么问题?需要改变什么?
  • 感谢@Rufus L 的回复。我想我只是希望方法能够像普通字典一样直接更新或添加到smallDict,而不是让smallDict 通过输入到bigDict
  • 一个“普通”的字典。如果它不需要与bigDict 相关(即您可以直接更新或添加它,而不需要bigDict),那么只需将其公开为它自己的属性。然后客户可以smallDict.Add("someKey", "someValue");
  • 执行set { smallDict = value.ToDictionary(x =&gt; x.Key, x =&gt; x.Value); } 之类的操作?
猜你喜欢
  • 2018-08-14
  • 1970-01-01
  • 2021-11-19
  • 2012-11-20
  • 2018-11-06
  • 2014-01-22
  • 2017-03-19
  • 2010-12-17
相关资源
最近更新 更多