【问题标题】:C# Dictionary equivalent to Python's get() methodC# 字典等价于 Python 的 get() 方法
【发布时间】:2015-01-29 16:59:02
【问题描述】:

在 Python 中,如果我有一个 dict,并且我想从一个可能不存在键的 dict 中获取一个值,我会这样做:

lookupValue = somedict.get(someKey, someDefaultValue)

如果someKey 不存在,则返回someDefaultValue

在 C# 中,TryGetValue() 有点相似:

var lookupValue;
if(!somedict.TryGetValue(someKey, lookupValue))
    lookupValue = someDefaultValue;

一个问题是,如果 someKeynull 则会引发异常,因此您需要进行空值检查:

var lookupValue = someDefaultValue;
if (someKey != null && !somedict.TryGetValue(someKey, lookupValue))
    lookupValue = someDefaultValue;

哪个,TBH,是 icky(3 行用于 dict 查找?)是否有更简洁(即 1 行)的方式,很像 Python 的 get()

【问题讨论】:

  • Afaik 没有这样的方法。但是您始终可以定义自己的扩展来包装这三行。或者您可以使用三元来获得单线,但这与您的方法没有什么不同
  • 我认为三元仍然不会是一个单行,因为你必须在 TryGetValue 之前声明 lookupValue 对吗?
  • @AdamParkin 是的,您必须首先声明传递给TryGetValue 的输出值是绝对正确的。尽管var value = key != null && dic.ContainsKey(key) ? dic[key] : defaultValue;,但您可以为单行执行此操作,这比TryGetValue 效率低,因为当键在字典中时它会进行两次查找。
  • 当然可以,但是字典键是散列的,所以它仍然是常数时间(即 O(1) + O(1) == O(1))对吧?
  • @AdamParkin 没错,复杂性并没有改变,但是做两次总是比做一次需要更长的时间。

标签: c# python dictionary


【解决方案1】:

这是使用TryGetValue 的正确方法。请注意,在调用TryGetValue 之前,您不能将返回值设置为默认值,而只需将其返回,TryGetValue 将在未找到密钥时将其重置为默认值。

public static TValue GetOrDefault<TKey, TValue>(
    this IDictionary<TKey, TValue> dictionary, 
    TKey key, 
    TValue defaultValue)
{
    TValue value;
    if(key == null || !dictionary.TryGetValue(key, out value))
        return defaultValue;
    return value;
}

当然,如果键是 null,您可能实际上想要一个例外,因为这对于字典来说总是无效的键。

现在可以使用 C# 7.0 内联输出变量声明将其简化为以下内容。

return key ==  null || !dictionary.TryGetValue(key, out var value)
    ? defaultValue
    : value;

【讨论】:

    【解决方案2】:

    TryGetValue 不是您正在寻找的方法。此方法返回一个布尔值。如果找到键则为真,如果没有则为假。Anf 如果找到键,它将将该值设置为您选择的变量。你想要的方法,在 Dictonary 类中不存在。 所以你有两种选择来解决这个问题:

    1. 检查是否存在:

      var value;
      if (somedic.TryGetValue("key", out value))
         lookupValue  = value;
      else
         lookupValue = defaultValue;
      
    2. 创建您自己的方法,如blog 所示。

    【讨论】:

    • 您还必须检查键是否为空,除非您已将键类型设置为不可为空的结构,(除非您想在键为空时抛出异常)。
    • 是的,没错。所以最好按照博客中的说明来实现它。
    • 博客也不测试密钥是否为空。该方法会为空键引发异常,但这可能是也可能不是正确的操作过程,但如果键为空,OP 似乎想要默认值。
    【解决方案3】:

    随着 7.1 对 lambda 和默认值以及 8 对 Nullability 的更多 C# 语言改进,Get() 函数现在可以编写为

    public static class Extentions
    {
        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        [return: NotNullIfNotNull("defaultValue")]
        public static TValue? Get<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue? defaultValue = default)
          => key == null || !dictionary.TryGetValue(key, out var value) ? defaultValue : value;
    
        /// <summary>
        /// Gets the value associated with the specified key and box/un-box correctly.
        /// </summary>
        [return: NotNullIfNotNull("defaultValue")]
        public static TTarget? GetWithConversion<TTarget, TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TTarget? defaultValue = default) where TTarget : TValue
          => (TTarget?)Convert.ChangeType(Get(dictionary, key, defaultValue), typeof(TTarget?), CultureInfo.InvariantCulture);
    }
    

    提供与重载的 Python 等效项大致相同的行为。我还使用 GetWithConversion 来处理对象装箱/拆箱。

    参考: => expression body, NotNullIfNotNull, default, Nullable reference types

    【讨论】:

    • 你希望keyTKey? 否则编译器会警告key == null 总是假的。
    猜你喜欢
    • 1970-01-01
    • 2010-12-05
    • 2015-02-24
    • 2016-07-21
    • 2011-10-09
    • 1970-01-01
    • 2017-10-14
    • 2014-02-26
    相关资源
    最近更新 更多