【问题标题】:Is it possible to create a 'self-initializing' Dictionary extension method in C# 6.0?是否可以在 C# 6.0 中创建“自初始化”字典扩展方法?
【发布时间】:2017-03-08 23:13:47
【问题描述】:

是否可以在 Dictionary 上创建一个扩展方法,如果 Dictionary 为 null 时能够自初始化?

我的想法大致如下:

public static void AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
    // Initialize the Dictionary if null
    dictionary = dictionary ?? new Dictionary<TKey, TValue>();

    // Update or insert value
    dictionary[key] = value;
}

实际使用示例:

// null Dictionary
Dictionary<string, string> dict = null;

// Self-initializing
dict.AddOrUpdate("Key", "Value");

或作为属性:

// Self-initializing
class.nullDict.AddOrUpdate("Key", "Value");

【问题讨论】:

  • 也许,但这打破了在空对象上调用方法的通常语义。通常应该抛出一个NullReferenceException。
  • @Amy 你不应该自己扔 NRE。 Throw an ArgumentNullException 代替。
  • 这不是我要说的,@CodeCaster。在空对象上调用方法应该会产生NullReferenceException。这打破了这种语义。我不是建议他扔任何东西。我建议他不要这样做。
  • 你的 if .. else... 块相当于 dictionary[key] = value
  • 即使这是可能的,我也同意这违反了“正常”语义。如果你只是看你的例子,我不希望最终得到一本字典。我希望它充其量是一个错误或无操作。只需正常初始化它,就可以省去其他人弄清楚它的麻烦。如果您尝试延迟初始化,请使用Lazy。

标签: c# .net dictionary extension-methods c#-6.0


【解决方案1】:

我认为这对使用它的每个人来说都非常令人困惑,因为对空对象执行操作并不常见。

所以即使有可能,我宁愿使用

Dictionary<string, string> dict = new Dictionary<string, string>();

此外,它现在甚至可以与属性一起使用。

【讨论】:

    【解决方案2】:

    没有

    为此,您必须通过ref 和that is not allowed 传递参数。

    当 this 关键字修改静态方法的第一个参数时,它会向编译器发出信号,表明该方法是扩展方法。 不需要或不允许在扩展方法的第一个参数上使用其他修饰符。

    【讨论】:

    • 即使使用属性来指示扩展方法,而不是使用 C# 的 this 语法,是否也是如此?
    • @Zev 是否​​有一个属性来指示扩展方法? AFAIK (this Foo bar) 参数语法是指示扩展方法的唯一方法。
    • @CodeCaster System.Runtime.CompilerServices.ExtensionAttribute。这是在没有专用扩展方法语法的 VB.NET 中定义扩展方法的唯一方法。据推测,当使用 C# this 语法时,编译器会应用该属性。
    • @Zev 好的,TIL。但是在 C# 中使用它会导致编译器错误(“不要直接使用System.Runtime.CompilerServices.ExtensionAttribute。使用参数修饰符 `this' 代替”)。
    • @CodeCaster 在 Github 上的 Roslyn 项目中提交 an issue。
    【解决方案3】:

    您可以,但您必须将返回类型从 void 更改为 Dictionay,这将是一个非常糟糕的 IMO 设计。每次使用扩展时,调用者都必须执行分配。

    public static IDictionary<TKey, TValue> AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
    {
        // Initialize the Dictionary if null
        if (dictionary == null)
            dictionary = new Dictionary<TKey, TValue>();
    
        // Update value if key already exists
        if (dictionary.ContainsKey(key))
            dictionary[key] = value;
        else
            // Insert value with the given key
            dictionary.Add(key, value);
    
        return dictionary;
    }
    

    【讨论】:

      【解决方案4】:

      我建议更改方法的签名:

      // Let's return the dictionary (either passed or created) 
      public static IDictionary<TKey, TValue> AddOrUpdate<TKey, TValue>(
          this IDictionary<TKey, TValue> dictionary, 
          TKey key, 
          TValue value) {
      
          IDictionary<TKey, TValue> result = dictionary;
      
          // Initialize the Dictionary if null
          if (result == null)
              result = new Dictionary<TKey, TValue>();
      
          // Update value if key already exists
          if (result.ContainsKey(key))
              result[key] = value;
          else
              // Insert value with the given key
              result.Add(key, value);
      
          return result;
      }
      

      ...

      Dictionary<string, string> dict = null;
      
      dict = dict.AddOrUpdate("Key", "Value");
      

      【讨论】:

        【解决方案5】:

        How can I get an extension method to change the original object? 中讨论了您的要求,结果是:在引用类型的情况下,您所能做的就是操作 对象。您不能分配它,因为那样您将处理本地副本,而不是最初传递给方法的对象:

        public static void Foo(this Object bar)
        {
            // assignment
            bar = new ...();
        
            // here you're NOT calling Baz() on the object passed into the method
            bar.Baz();
        }
        

        请注意,这些是引用类型语义。你甚至不能对值类型做任何有意义的事情:

        public static void PlusOne(this int number)
        {
            number++;
        }
        

        这永远不会修改原始值,因为它们是按定义按值传递的:此扩展方法对调用它的值的副本进行操作。

        话虽如此,这更像是一个关于期望的问题。任何阅读此代码的人:

        Dictionary<string, string> dict = null;
        dict.AddOrUpdate("Key", "Value");
        

        期望NullReferenceException(如果AddOrUpdate() 是Dictionary&lt;TKey, TValue&gt; 的成员方法)或ArgumentNullException in the case of an extension method 和even the compiler would expect that。

        你认为你需要这个是有根本原因的。当然,实现 “从扩展方法返回新的或更新的字典并在每次修改时分配” 的答案是可行的,但会给你一个非常笨拙的语法:

        Dictionary<string, string> dict = null;
        dict = dict.AddOrUpdate("Key", "Value");
        

        而且您只需忘记不寻常的dict = 一次 即可在超出其使用范围的某个时间点获得 NRE/ANE。如果您从事函数式编程,那可能没问题,但 C# 不是。

        所以:确保在尝试添加键值对之前初始化字典,并且不再需要所有这些异常。可能就这么简单:

        Dictionary<string, string> dict = possiblyNullVariable ?? new Dictionary<string, string>();
        

        【讨论】:

          【解决方案6】:

          我认为你想要的是一个自初始化属性:

          private Dictionary<string, string> dict;
          
          public Dictionary<string, string> Dict => this.dict ?? (this.dict = new Dictionary<string, string>());
          

          那么,无论何时使用Dict,都保证会被初始化。

          【讨论】:

            猜你喜欢
            • 2015-06-11
            • 1970-01-01
            • 2022-08-14
            • 2021-03-26
            • 2018-11-30
            • 1970-01-01
            • 2011-08-26
            • 1970-01-01
            • 2012-05-18
            相关资源
            最近更新 更多