参数的可空性与约束类型不匹配

时间:2020-03-13 10:19:00

标签: c# nullable-reference-types

我编写了以下扩展方法:

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));
    }
}

这节省了许多编写INPC感知属性的样板。

但是,我现在在调用NotifyChanged时收到编译器警告错误:

“字符串”类型?不能在通用类型或方法'INotifyPropertyChangedExtensions.NotifyChanged(INotifyPropertyChanged,ref T,T,Action,string?)'中用作类型参数'T'。类型参数“字符串”的可空性与约束类型“ System.IEquatable”不匹配。

AFAICT错误是说string?不能强制转换为IEquatable<string?>,只能将string强制强制转换为IEquatable<string>

我该如何解决?应用一些属性?还是其他?

1 个答案:

答案 0 :(得分:4)

您的问题是:

where T : IEquatable<T>

这表示T必须是不可为空的IEquatable<T>。您希望它可以为空。您可以通过添加?来表示:

where T : IEquatable<T>?

请注意,这将在if (current.Equals(newValue))上进行投诉,如果currentnull,则会抛出该错误。


执行此操作的通常方法不是通过将T约束为IEquatable<T>,而是使用EqualityComparer<T>.Default。如果T实现了IEquatable<T>,这将为您提供一个相等比较器,该比较器将调用IEquatable<T>.Equals,否则将退回到调用普通的object.Equals

如果currentnull,这也解决了您的NRE问题:

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调用。 / p>

如果您确实希望通过筹款活动来参加比赛,则只需传递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);