Обнуляемость аргумента не соответствует типу ограничения - PullRequest
0 голосов
/ 13 марта 2020

Я написал следующий метод расширения:

public static void NotifyChanged<T>(this INotifyPropertyChanged inpc, ref T current, T newValue, Action<PropertyChangedEventArgs> eventRaiser, [CallerMemberName] string? name = null) where T : IEquatable<T> {
    if (current.Equals(newValue)) { return; }
    current = newValue;
    eventRaiser(new PropertyChangedEventArgs(name));
}

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

public class Foo : Bar, INotifyPropertyChanged {
    public event PropertyChangedEventHandler? PropertyChanged;

    private string? rootExpression;

    public string? RootExpression {
        get => rootExpression;
        set => this.NotifyChanged(ref rootExpression, value, args => PropertyChanged?.Invoke(this, args));
    }
}

Это экономит большую часть шаблонного письма INP C - в курсе свойств.

Однако теперь я получаю сообщение об ошибке предупреждения компилятора при вызове NotifyChanged:

Тип 'строка?' не может использоваться в качестве параметра типа 'T' в универсальном типе или методе c INotifyPropertyChangedExtensions.NotifyChanged (INotifyPropertyChanged, ref T, T, Action, string?) '. Обнуляемость аргумента типа 'строка?' не соответствует типу ограничения 'System.IEquatable'.

AFAICT ошибка говорит о том, что string? нельзя преобразовать в IEquatable<string?>, только string можно преобразовать в IEquatable<string>.

Как я могу решить эту проблему? Применить какой-нибудь атрибут? Или что-то еще?

1 Ответ

3 голосов
/ 13 марта 2020

Ваша проблема:

where T : IEquatable<T>

Это говорит о том, что T должен быть не обнуляемым IEquatable<T>. Вы хотите, чтобы это было обнуляемым. Вы можете сказать это, добавив ?:

where T : IEquatable<T>?

Обратите внимание, что тогда будет выдан запрос на if (current.Equals(newValue)), который выдаст, если current равен null.


Обычный способ сделать это не ограничить T быть IEquatable<T>, а использовать EqualityComparer<T>.Default. Если T реализует IEquatable<T>, это дает вам средство сравнения равенства, которое вызывает IEquatable<T>.Equals, в противном случае он возвращается к вызову обычного object.Equals.

. Это также решает вашу проблему NRE, если current is null:

public static void NotifyChanged<T>(
    this INotifyPropertyChanged inpc,
    ref T current, T newValue,
    Action<PropertyChangedEventArgs> eventRaiser,
    [CallerMemberName] string? name = null)
{
    if (EqualityComparer<T>.Default.Equals(current, newValue)) { return; }
    current = newValue;
    eventRaiser(new PropertyChangedEventArgs(name));
}

Также редко передается eventRaiser: обычно вы делаете NotifyChanged метод в базовом классе, который реализует INotifyPropertyChanged, а не делая это методом расширения. Затем вы можете просто позволить NotifyChanged вызвать событие PropertyChanged или написать другой метод, например OnPropertyChanged, который вызывает PropertyChanged, и вызывать его из NotifyChanged.

Если вы хотите передать в случае, чтобы поднять, вы можете просто передать в PropertyChangedEventHandler:

public static void NotifyChanged<T>(
    this INotifyPropertyChanged _,
    ref T current, T newValue,
    PropertyChangedEventHandler eventHandler,
    [CallerMemberName] string? name = null)
{
    if (EqualityComparer<T>.Default.Equals(current, newValue)) { return; }
    current = newValue;
    eventHandler?.Invoke(this, new PropertyChangedEventArgs(name));
}

this.NotifyChanged(ref rootExpression, value, PropertyChanged);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...