3

我的代码看起来像这样:

#nullable enable
class MyClass<KEY, ITEM>
{
    readonly Dictionary<KEY, ITEM> Map = new Dictionary<KEY, ITEM>();
    public void Process(KEY key, ITEM item)
    {
        if (key != null)
        {
            Map[key] = item;
        }
    }
}
#nullable disable

编译器对此并不感兴趣,它给了我警告

type 'KEY' cannot be used as type parameter 'TKey' in the generic type or method 'Dictionary<TKey, TValue>

我当然可以理解。问题是,向 Process() 发送 'key' 参数的 null 是完全有效的,因此我无法将“where KEY: notnull”约束添加到类中。(并且 MyClass 需要接受 KEY 类型参数的类和结构)

我唯一能想到的是:

#nullable enable
class MyClass<KEY, ITEM>
{
#nullable disable
    readonly Dictionary<KEY, ITEM> Map = new Dictionary<KEY, ITEM>();
#nullable enable
    public void Process(KEY key, ITEM item)
    {
        if (key != null)
        {
            Map[key] = item;
        }
    }
}
#nullable disable

这让编译器很高兴,但是我没有那些漂亮的 C# 8 空检查。例如,它允许我编写以下代码:

Map[default] = item;

并且编译器不会眨眼。

如何告诉编译器 Dictionary<> 的 'KEY' 类型参数应该不允许空值,但仍允许外部类中的 KEY 值为空?

编辑

我想使用新的 C# 8 可空性功能,以便在编译时捕获尽可能多的空指针(而不是等待运行时异常)。

进一步编辑

我现在的方向是在 Dictionary 周围放置一个薄层以强制执行 null 限制并使用它而不是 Dictionary<>

#nullable enable
public class CheckDictionary<KEYTYPE, VALUETYPE>
{
#nullable disable
    readonly Dictionary<KEYTYPE, VALUETYPE> Dictionary = new Dictionary<KEYTYPE, VALUETYPE>();
#nullable enable

    public VALUETYPE this[[DisallowNull] KEYTYPE key]
    {
        get { return Dictionary[key]; }
        set { Dictionary[key] = value; }
    }

    public bool Remove([DisallowNull] KEYTYPE key)
    { return Dictionary.Remove(key); }

    public bool TryGetValue([DisallowNull] KEYTYPE key, out VALUETYPE value)
    { return Dictionary.TryGetValue(key, out value); }

    public List<VALUETYPE> Values => Dictionary.Values.ToList();
}
4

2 回答 2

1

我认为在您的情况下可以使用下一种方法:

  • 将类型参数约束TKeynotnull。因此,编译器将对TKey.
  • 添加AllowNullAttribute到方法的参数TKey keyProcess。因此,传递null key给该方法Process不会产生警告。

这是带有注释的代码:

class MyClass<TKey, TItem> where TKey : notnull
{
    // With "notnull" constraint type parameter "TKey" matches type constraint
    // of the class Dictionary<TKey, TValue>, therefore compiler does not
    // generate the next warning:
    //   The type 'TKey' cannot be used as type parameter 'TKey' in the 
    //   generic type or method 'Dictionary<TKey, TValue>'. Nullability
    //   of type argument 'TKey' doesn't match 'notnull' constraint.
    readonly Dictionary<TKey, TItem> Map = new Dictionary<TKey, TItem>();

    public void Process([System.Diagnostics.CodeAnalysis.AllowNull] TKey key, TItem item)
    {
        // "TKey key" is marked with [AllowNull] attribute. Therefore if you delete
        // null check "key != null" compiler will produce the next warning on the line
        // "Map[key] = item":
        //   Possible null reference argument for parameter 'key' in
        //   'TItem Dictionary<TKey, TItem>.this[TKey key]'.
        if (key != null) 
            Map[key] = item;

        // Because "TKey" is constrained to be "notnull", this line of code
        // produces the next warning:
        //   Possible null reference argument for parameter 'key' in
        //   'TItem Dictionary<TKey, TItem>.this[TKey key]'.
        Map[default] = item;
    }
}

static class DemoClass
{
    public static void Demo()
    {
        MyClass<string, int> mc1 = new MyClass<string, int>();
        // This line does not produce a warning, because "TKey key" is marked
        // with [AllowNull] attribute.
        mc1.Process(null, 0);
        // This line does not produce a warning too.
        mc1.Process(GetNullableKey(), 0);

        // Usage of "MyClass" with value type "TKey" is also allowed.
        // Compiler does not produce warnings.
        MyClass<int, int> mc2 = new MyClass<int, int>();
        mc2.Process(0, 1);
    }

    public static string? GetNullableKey() => null;
}

因此,使用这种方法,我们:

  • TKeyMyClass;中强制执行空检查
  • 允许传递null key给该Process方法而不会收到警告。
于 2020-06-19T03:59:56.570 回答
0

我发现了同样的问题,我的解决方案是将密钥包装在一个 1 元组中:

class MyClass<TKey, TItem>
{
    readonly Dictionary<ValueTuple<TKey>, TItem> Map = new Dictionary<ValueTuple<TKey>, TItem>();

    public void Process(TKey key, TItem item)
    {
        Map[ValueTuple.Create(key)] = item;
    }
}

通过这种方式,可以将任何值添加到字典中(即 null),并且编译器在不禁用规则的情况下得到满足。

于 2021-03-02T03:29:29.163 回答