C# 只读集合属性的类设计

C# 只读集合属性的类设计,c#,C#,我有一个IDictionary集合,需要将它传递给一个类,以便将其包装成IReadOnlyDictionary(注意MyEnum,但不是MyEnum?) 我提出了两种设计: 延迟包装到IReadOnlyDictionary,直到属性访问: public class MyClass { private readonly IEnumerable<KeyValuePair<string, MyEnum?>> _kvps; public MyClass(IEnu

我有一个
IDictionary
集合,需要将它传递给一个类,以便将其包装成
IReadOnlyDictionary
(注意
MyEnum
,但不是
MyEnum?

我提出了两种设计:

  • 延迟包装到
    IReadOnlyDictionary
    ,直到属性访问:

    public class MyClass
    {
        private readonly IEnumerable<KeyValuePair<string, MyEnum?>> _kvps;
    
        public MyClass(IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
        {
            _kvps = kvps;
        }
    
        public IReadOnlyDictionary<string, MyEnum> Kvps
        {
            get
            {
                var filtered = from kvp in _kvps
                               where kvp.Value.HasValue
                               select kvp;
                return new ReadOnlyDictionary<string, MyEnum>(
                   filtered.ToDictionary(kvp => kvp.Key, kvp => (MyEnum)kvp.Value);
            }
        }
    }
    
    公共类MyClass
    {
    私有只读IEnumerable_kvps;
    公共MyClass(IEnumerable kvps)
    {
    _kvps=kvps;
    }
    公共IReadOnlyDictionary Kvps
    {
    得到
    {
    过滤的var=来自kvp中的_kvps
    其中kvp.Value.HasValue
    选择kvp;
    返回新的ReadOnlyDictionary(
    ToDictionary(kvp=>kvp.Key,kvp=>(MyEnum)kvp.Value);
    }
    }
    }
    
  • 急切地评估构造函数中的集合

    public class MyClass
    {
        public MyClass(IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
        {
            Kvps = ToReadOnly(kvps);
        }
    
        public IReadOnlyDictionary<string, MyEnum> Kvps { get; }
    
        private static IReadOnlyDictionary<string, MyEnum> ToReadOnly(
            IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
        {
            var filtered = from kvp in kvps
                           where kvp.Value.HasValue
                           select kvp;
            return new ReadOnlyDictionary<string, MyEnum>(
               filtered.ToDictionary(kvp => kvp.Key, kvp => (MyEnum)kvp.Value);
        }
    }
    
    公共类MyClass
    {
    公共MyClass(IEnumerable kvps)
    {
    Kvps=toreadoly(Kvps);
    }
    公共IReadOnlyDictionary Kvps{get;}
    私有静态IReadOnlyDictionary ToReadOnly(
    IEnumerable(kvps)
    {
    过滤的var=来自kvp(单位:kvps)
    其中kvp.Value.HasValue
    选择kvp;
    返回新的ReadOnlyDictionary(
    ToDictionary(kvp=>kvp.Key,kvp=>(MyEnum)kvp.Value);
    }
    }
    
  • 框架设计指南的这一部分建议在构造函数中完成最少的工作,因此我选择第一种方法。然而,这意味着每次调用
    MyClass.Kvps
    都会触发
    \u Kvps
    的副本,这并不理想

    我想知道在以下方面哪种方法更好(或有其他方法):

    • 内存效率(理想情况下,只有一个集合副本存储在
      MyClass
    • 性能(属性访问应快速,且不应触发
      KeyValuePair
      s)的副本)

    编辑:如果需要最新的源代码值,最好的方法是实现自己的类,该类实现了
    IReadOnlyDictionary
    。使用
    ReadOnlyDictionary
    的私有字段初始化它。每次调用都会进行查找,如果键存在并且
    HasValue
    ,则返回值

    请注意,此实现依赖于作为
    IReadOnlyDictionary
    传入的原始值的引用,以避免复制值


    公共类MyReadOnlyDictionary:IReadOnlyDictionary,其中TValue:struct
    {
    //这里要实现的其他方法。。。
    公共MyReadOnlyDictionary(IReadOnlyDictionary kvps)
    {
    _kvps=kvps;
    }
    私人IREADONLYDICTIONAL kvps;
    新的公共TValue此[TKey]
    {
    得到
    {
    TValue?val=_kvps[键];
    if(val.HasValue)
    返回值;
    抛出新的KeyNotFoundException();
    }
    }
    }
    
    在这两个要求中—不要复制键值对,也不要存储两个副本—您必须断开一个

    让我们看到这一点并认为一定有解决办法的原因是,我们看到了
    TValue
    TValue?
    ,我们的大脑希望看到它们属于同一类型,但它们不是同一类型

    如果您设想这是两种不同的类型,而不是
    TValue
    TValue?
    ,例如
    int
    string
    ,我们希望在过滤时将一种类型的集合投影到另一种类型的集合,那么情况就更清楚了。例如

    List<string> GetStringsFromNonNegativeInts(List<int> ints)
    {
        return ints.Where(i=>i>-1).Select(i=>i.ToString()).ToList();
    }
    
    List GetStringsFromNonNegativeInts(List ints)
    {
    返回int.Where(i=>i>-1);
    }
    
    这与尝试将一组
    TValue?
    过滤为一组
    TValue
    完全相同,即使没有字典也一样。这更难看到。
    TValue
    TValue?
    代码使我们失明


    只有两种方法可以做到这一点。一种是每次复制,另一种是保持两个列表同步。

    如果在构造和属性访问之间或在连续属性访问之间修改源词典,则您的两个设计会有不同的行为。您想要哪种方法?@Blorgbeard我想将属性设置为always反映源代码
    IDictionary
    中的最新值。这是否意味着我仅限于我的第一种方法?然后是的,您必须在属性访问器中进行筛选。除非您编写自己的
    IReadOnlyDictionary
    类来包装源代码。那么至少您只有一个数据副本,但您有要进行的ore筛选(几乎所有自定义IReadOnlyDictionary上的属性访问)@Blorgbeard我同意这可能是最好的方法。但是,如果将对值的引用作为
    IDictionary
    传入,则可以最小化筛选。OP在其评论中表示,他希望在每次调用属性时都使用最新的源值,因此缓存将不起作用。很好的一点。我更新了答案以反映这一点。我认为c上面提到的关于实现一个新的IReadOnlyDictionary实现类是最有效的方法。我也研究了这种方法。处理枚举数似乎很痛苦,因为你不得不“假装”有些条目实际上不在词典中。@ScottHannen这取决于您如何看待它。对于使用此只读词典的用户来说,一个带有空值
    的条目对他们来说并不存在,也就是说,它对他们没有任何用处,他们可能会忽略它。这个新类只是一个映射来反映这一点。我们不从这个意义上说,我们可以对他们“隐藏”任何他们无论如何都不会忽略的东西。它只是封装了忽略空值的逻辑。必须通过实现
    IReadOnlyDictionary
    并测试它的行为来证明这一点
    List<string> GetStringsFromNonNegativeInts(List<int> ints)
    {
        return ints.Where(i=>i>-1).Select(i=>i.ToString()).ToList();
    }