Func 参数的通用 NotNullWhen
Generic NotNullWhen for Func argument
我有这样的功能:
public class X {
public T GetOrNew<T, TKey>(TKey? key, Func<TKey?, T> factory){
// omitted code
var instance = factory(key);
// omitted code
return instance
}
}
现在的问题是以下用法会生成可为空的警告:
// Here key is never allowed to be null.
public Person GetPerson(StreetIndex key){
_ = key ?? throw new ArgumentNullException(paramName: nameof(key));
var x = new X();
// so here key is never null, so index is never null
x.GetOrNew(key, index => index.GetRandomPerson()); // warning on index.GetRandomPerson because it could be null according to the signature.
// this call should also work and not give any nullable warnings.
x.GetOrNew<StreetIndex, Person>(null, _ => new PersonFactory.Birth());
}
当然,看代码就会清楚,当 GetOrNew
的参数是 null
时,传递给工厂的 key
只是 null
。
是否有任何属性(或模式)表明这一点?
对于 Person? Mogrify(StreetIndex? key)
等“正常”功能,我可以使用 [return: NotNullIfNotNull(nameof(key))
。
请注意,我确实需要支持 null
作为 GetOrNew
的有效参数。
您当前的签名是说“即使 TKey
是不可为 null 的类型,key
并且 factory
参数也可以为 null”。
相反,您可以声明您的方法:
public T GetOrNew<T, TKey>(TKey key, Func<TKey, T> factory)
这表示“当 TKey
是可空类型时,则 key
并且 factory
参数可以为空;否则,如果 TKey
是不可空类型, 然后 key
并且 factory
参数不能为 null".
这意味着这是允许的:
AnotherClass? key = null;
GetOrNew(key, theKey => new AnotherClass());
并且这不会生成可为 null 的警告:
AnotherClass key = new AnotherClass();
GetOrNew(key, theKey => theKey.DoIt());
注意:
GetOrNull(null, _ => ...);
永远无法工作,无论是否为空。编译器需要推断 TKey
的类型,而您提供的唯一信息是它可以是 null
,这不足以让它继续。
您需要明确指定 TKey
:
GetOrNull<StreetIndex?, Person>(null, _ => ...);
或使用类型化的 null:
GetOrNull((StreetIndex?)null, _ => ...)
GetOrNull(default(StreetIndex?), _ => ...)
或者实际上,在factory
参数中给出类型信息:
GetOrNull(null, (StreetIndex? _) => ...)
那么如果 theKey
为空会发生什么?它会抛出异常。你希望发生什么?例如你可以 return 一个新对象
x.GetOrNew(key, theKey => theKey?.DoIt() ?? new();
编辑:您似乎想让用户灵活地调用 GetOrNew
。您可以在该方法的实现中进行空检查,例如
var instance = key is not null ? factory(key) : new();
请注意,GetOrNew(null, _ => new Whatever())
可能不起作用,因为无法从第一个参数确定 TKey
的类型。 (最好是 object?
)。
您可以添加一个重载来支持:
public class X
{
#nullable enable
public T GetOrNew<T, TKey>(TKey? key, Func<TKey?, T> factory)
where T : class, new()
{
// omitted code
var instance = key is not null ? factory(key) : new();
// omitted code
return instance;
}
public T GetOrNew<T>(object? key, Func<object?, T> factory)
where T : class, new() =>
GetOrNew<T, object?>(key, factory);
#nullable restore
}
我有这样的功能:
public class X {
public T GetOrNew<T, TKey>(TKey? key, Func<TKey?, T> factory){
// omitted code
var instance = factory(key);
// omitted code
return instance
}
}
现在的问题是以下用法会生成可为空的警告:
// Here key is never allowed to be null.
public Person GetPerson(StreetIndex key){
_ = key ?? throw new ArgumentNullException(paramName: nameof(key));
var x = new X();
// so here key is never null, so index is never null
x.GetOrNew(key, index => index.GetRandomPerson()); // warning on index.GetRandomPerson because it could be null according to the signature.
// this call should also work and not give any nullable warnings.
x.GetOrNew<StreetIndex, Person>(null, _ => new PersonFactory.Birth());
}
当然,看代码就会清楚,当 GetOrNew
的参数是 null
时,传递给工厂的 key
只是 null
。
是否有任何属性(或模式)表明这一点?
对于 Person? Mogrify(StreetIndex? key)
等“正常”功能,我可以使用 [return: NotNullIfNotNull(nameof(key))
。
请注意,我确实需要支持 null
作为 GetOrNew
的有效参数。
您当前的签名是说“即使 TKey
是不可为 null 的类型,key
并且 factory
参数也可以为 null”。
相反,您可以声明您的方法:
public T GetOrNew<T, TKey>(TKey key, Func<TKey, T> factory)
这表示“当 TKey
是可空类型时,则 key
并且 factory
参数可以为空;否则,如果 TKey
是不可空类型, 然后 key
并且 factory
参数不能为 null".
这意味着这是允许的:
AnotherClass? key = null;
GetOrNew(key, theKey => new AnotherClass());
并且这不会生成可为 null 的警告:
AnotherClass key = new AnotherClass();
GetOrNew(key, theKey => theKey.DoIt());
注意:
GetOrNull(null, _ => ...);
永远无法工作,无论是否为空。编译器需要推断 TKey
的类型,而您提供的唯一信息是它可以是 null
,这不足以让它继续。
您需要明确指定 TKey
:
GetOrNull<StreetIndex?, Person>(null, _ => ...);
或使用类型化的 null:
GetOrNull((StreetIndex?)null, _ => ...)
GetOrNull(default(StreetIndex?), _ => ...)
或者实际上,在factory
参数中给出类型信息:
GetOrNull(null, (StreetIndex? _) => ...)
那么如果 theKey
为空会发生什么?它会抛出异常。你希望发生什么?例如你可以 return 一个新对象
x.GetOrNew(key, theKey => theKey?.DoIt() ?? new();
编辑:您似乎想让用户灵活地调用 GetOrNew
。您可以在该方法的实现中进行空检查,例如
var instance = key is not null ? factory(key) : new();
请注意,GetOrNew(null, _ => new Whatever())
可能不起作用,因为无法从第一个参数确定 TKey
的类型。 (最好是 object?
)。
您可以添加一个重载来支持:
public class X
{
#nullable enable
public T GetOrNew<T, TKey>(TKey? key, Func<TKey?, T> factory)
where T : class, new()
{
// omitted code
var instance = key is not null ? factory(key) : new();
// omitted code
return instance;
}
public T GetOrNew<T>(object? key, Func<object?, T> factory)
where T : class, new() =>
GetOrNew<T, object?>(key, factory);
#nullable restore
}