参数的可空性与约束类型不匹配
Nullability of argument doesn't match constraint type
我写了如下扩展方法:
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
时收到编译器警告错误:
The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'INotifyPropertyChangedExtensions.NotifyChanged(INotifyPropertyChanged, ref T, T, Action, string?)'. Nullability of type argument 'string?' doesn't match constraint type 'System.IEquatable'.
AFAICT 错误是说 string?
不能转换为 IEquatable<string?>
,只有 string
可以转换为 IEquatable<string>
。
我该如何解决这个问题?应用一些属性?或者别的什么?
您的问题是:
where T : IEquatable<T>
这表示 T
必须是不可为 null 的 IEquatable<T>
。您希望它可以为空。你可以通过添加 ?
:
来表示
where T : IEquatable<T>?
请注意,如果 current
为 null
。
,则会在 if (current.Equals(newValue))
上报错
通常的做法不是将 T
限制为 IEquatable<T>
,而是使用 EqualityComparer<T>.Default
。如果 T
实现 IEquatable<T>
,这会为您提供一个调用 IEquatable<T>.Equals
的相等比较器,否则它会退回到调用正常的 object.Equals
.
如果 current
是 null
:
,这也解决了您的 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
:通常你会在实现 INotifyPropertyChanged
的基础 class 上创建 NotifyChanged
一个方法,而不是将其作为一个方法扩展方法。然后你可以让 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);
我写了如下扩展方法:
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
时收到编译器警告错误:
The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'INotifyPropertyChangedExtensions.NotifyChanged(INotifyPropertyChanged, ref T, T, Action, string?)'. Nullability of type argument 'string?' doesn't match constraint type 'System.IEquatable'.
AFAICT 错误是说 string?
不能转换为 IEquatable<string?>
,只有 string
可以转换为 IEquatable<string>
。
我该如何解决这个问题?应用一些属性?或者别的什么?
您的问题是:
where T : IEquatable<T>
这表示 T
必须是不可为 null 的 IEquatable<T>
。您希望它可以为空。你可以通过添加 ?
:
where T : IEquatable<T>?
请注意,如果 current
为 null
。
if (current.Equals(newValue))
上报错
通常的做法不是将 T
限制为 IEquatable<T>
,而是使用 EqualityComparer<T>.Default
。如果 T
实现 IEquatable<T>
,这会为您提供一个调用 IEquatable<T>.Equals
的相等比较器,否则它会退回到调用正常的 object.Equals
.
如果 current
是 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
:通常你会在实现 INotifyPropertyChanged
的基础 class 上创建 NotifyChanged
一个方法,而不是将其作为一个方法扩展方法。然后你可以让 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);