Что ?[]? синтаксис в C #?

85

Пока я изучал делегат, который на самом деле является абстрактным классом Delegate.cs, я увидел следующий метод, в котором я не понимаю

  • Почему возвращаемое значение использует, ?хотя это уже ссылочный ( класс ) тип
  • ?[]? смысл по параметру

Могли бы вы объяснить?

public static Delegate? Combine(params Delegate?[]? delegates)
{
    if (delegates == null || delegates.Length == 0)
        return null;

    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}
snr - Восстановить Монику
источник
23
Разве это не обнуляемый массив, который может содержать обнуляемые значения?
Сид
3
c # 8, теперь вы можете указать, что объектная переменная не может быть нулевой. Если вы перевернете этот флаг компилятора, вы должны указать каждую переменную, которая может быть нулевой.
Джереми

Ответы:

66

Пошаговое объяснение:

params Delegate?[] delegates - это массив обнуляемый Delegate

params Delegate?[]? delegates - весь массив может быть обнуляемым

Поскольку каждый параметр имеет тип, Delegate?а вы возвращаете индекс Delegate?[]?массива, то имеет смысл, что возвращаемый тип - это Delegate?иначе, компилятор вернет ошибку, как если бы вы выполняли ретрансляцию, и intиз метода, который возвращает строку.

Например, вы можете изменить свой код, чтобы он возвращал такой Delegateтип:

public static Delegate Combine(params Delegate?[]? delegates)
{
    Delegate defaulDelegate = // someDelegate here
    if (delegates == null || delegates.Length == 0)
        return defaulDelegate;

    Delegate d = delegates[0] ?? defaulDelegate;
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}
Атанасиос Катарас
источник
4
как насчет моего первого вопроса? Почему возвращаемое значение использует, ?хотя это уже ссылочный (класс) тип
snr - Восстановить Монику
2
Потому что вы возвращаете D, который является делегатом? тип. А также null или Delegate в зависимости от параметров
Athanasios Kataras
2
@snr Возвращаемое значение использует ?точно по той же причине, по которой значение аргумента использует ?. Если вы понимаете последнее, вы автоматически понимаете первое. Поскольку метод может возвращать ноль , как параметр может принимать ноль . То есть они оба могут содержать ноль .
GSerg
2
Я коснулся этого. Я просто ответил как вторая часть с примером. Since each parameter is of the type Delegate? and you return an index of the Delegate?[]? array, then it makes sense that the return type is Delegate?
Атанасиос Катарас
2
@snr: почему используется возвращаемое значение? хотя это уже ссылочный (класс) тип (1) Это может быть привычкой или остатком рефакторинга, если структуры также используются в аналогичном коде (2) В C # 8 ссылочные типы могут быть запрещены по своей сути обнуляемыми (таким образом, требуя явной обнуляемости ( 3) Foo?имеет аккуратное HasValueсвойство, в то время как его Fooнеобходимо == nullпроверить (4) Он сообщает разработчикам, которые читают сигнатуру метода, о том, что нулевые значения - это возможный, ожидаемый и правильный результат. (5) Код выглядит написанным кем-то, кто очень любит обнуляемость и откровенен в этом
Флатер
25

Обнуляемые ссылочные типы являются новыми в C # 8.0, они не существуют раньше.

Это вопрос документации и того, как создаются предупреждения во время компиляции.

Исключение «объект не установлен как экземпляр объекта» является довольно распространенным явлением. Но это исключение времени выполнения, его можно частично обнаружить уже во время компиляции.

Для регулирования Delegate dвы всегда можете позвонить

 d.Invoke();

это означает, что вы можете кодировать его, во время компиляции ничего не произойдет. Это может вызвать исключения во время выполнения.

Пока для нового Delegate? pэтот кодекс

 p.Invoke();

выдаст предупреждение компилятора. CS8602: Dereference of a possibly null reference если вы не напишите:

 p?.Invoke();

что значит, звоните только если не ноль.

Таким образом, вы документируете переменную может содержать ноль или нет. Он вызывает предупреждения раньше и может избежать нескольких тестов на ноль. То же самое, что у вас есть для int и int? Вы точно знаете, что одно не равно нулю - и вы знаете, как преобразовать одно в другое.

Holger
источник
с предостережением, которое вы не знаете наверняка, Delegateкоторое не является нулевым. Вы просто притворяетесь, что знаете наверняка (что в большинстве случаев достаточно).
Тим
@TimPohlmann Так же, как int i = null невозможен, также невозможна строка s = null (в C # 8 с этим критическим изменением). Так что это немного больше, чем притворяться. Для обратной совместимости он понижен до предупреждения. Если вы обновите предупреждение до ошибки, вы точно знаете, что оно не равно нулю.
Хольгер
4
В прошлый раз, когда я проверял NRT, это не 100% пуленепробиваемый. Есть определенные крайние случаи, которые компилятор не может обнаружить.
Тим
Да, но это то же самое, что и для предупреждений «переменная не инициализирована» или «не все пути кода возвращают значение». Это все 100% функций времени компиляции, без обнаружения во время выполнения. В отличие от int ?, я думаю, что они не добавляют дополнительную память для хранения дополнительной информации. Итак, скажем, это так же надежно, как intellisense. Довольно хорошо.
Хольгер
1
Если у вас есть, intоно никогда не может быть нулевым. Если у вас есть, Delegateон может быть нулевым (по разным причинам, например, рефлексия). Обычно можно предположить, что Delegate(в C # 8 с включенным NRT) не ноль, но вы никогда не знаете наверняка (откуда intмы точно знаем).
Тим
5

В C # 8 следует явно помечать ссылочные типы как обнуляемые.

По умолчанию эти типы не могут содержать ноль, что-то вроде типов значений. Хотя это не меняет того, как все работает под капотом, проверка типов потребует, чтобы вы делали это вручную.

Данный код подвергается рефакторингу для работы с C # 8, но он не получает преимущества от этой новой функции.

public static Delegate? Combine(params Delegate?[]? delegates)
{
    // ...[]? delegates - is not null-safe, so check for null and emptiness
    if (delegates == null || delegates.Length == 0)
        return null;

    // Delegate? d - is not null-safe too
    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}

Вот пример обновленного кода (не работает, просто идея), использующего эту функцию. Это спасло нас от нулевой проверки и немного упростило этот метод.

public static Delegate? Combine(params Delegate[] delegates)
{
    // `...[] delegates` - is null-safe, so just check if array is empty
    if (delegates.Length == 0) return null;

    // `d` - is null-safe too, since we know for sure `delegates` is both not null and not empty
    Delegate d = delegates[0];

    for (int i = 1; i < delegates.Length; i++)
        // then here is a problem if `Combine` returns nullable
        // probably, we can add some null-checks here OR mark `d` as nullable
        d = Combine(d, delegates[i]);

    return d;
}
amankkg
источник
2
those types are not able to contain nullОбратите внимание, что он генерирует предупреждение компилятора , а не ошибку. Код будет работать нормально (хотя он может генерировать исключения NullReferenceExceptions). Это сделано с учетом обратной совместимости.
JAD