c# 8 обнуляемый + Словарь <> - PullRequest
2 голосов
/ 18 июня 2020

Мой код выглядит примерно так:

#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>

, которое я могу понять. Проблема в том, что отправка null для параметра 'key' в Process () совершенно допустима, поэтому я не могу добавить к классу ограничение 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;

, и компилятор не моргнет глазом.

Как я могу сообщить компилятору, что параметр типа KEY должен Dictionary <> должен запрещать нулевые значения, но все же разрешать значениям KEY быть нулевыми во внешнем классе? как можно больше нулевых указателей во время компиляции (вместо ожидания исключений во время выполнения). нулевые ограничения и использовать его вместо 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();
}

1 Ответ

1 голос
/ 19 июня 2020

Я думаю, что в вашем случае можно использовать следующий подход:

  • Параметр типа ограничения TKey должен быть notnull. В результате компилятор выполнит нулевые проверки для TKey.
  • Добавьте AllowNullAttribute к параметру TKey key метода Process. В результате код, передающий 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;
}

Таким образом, используя такой подход, мы:

  • включили нулевые проверки против TKey в MyClass;
  • разрешено пройти null key по методу Process без предупреждений.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...