如何检查是否使用可空泛型参数调用泛型函数
How to check if generic function is called with nullable generic argument
我正在尝试编写一个行为不同的通用函数,具体取决于它是否使用允许空值的参数调用。
在 C#8 之前的世界中,我使用的是这样的东西:
T MyFunc<T>()
{
object value = SomeExternalFunctionCall();
if (value == null)
{
if (typeof(T).AcceptsNulls())
{
return null;
}
else
{
throw new Exception($"{typeof(T).Name} does not support nulls");
}
}
return (T)value;
}
其中 AcceptsNulls()
是这样的:
public static bool IsAcceptNulls(this Type type)
{
if (type.IsValueType)
{
if (Nullable.GetUnderlyingType(type) != null)
{
return true;
}
return false;
}
return true;
}
因此 MyFunc
对引用类型和值类型都能愉快地工作,并适当支持可为 null 的值类型。
但现在我想将其包含在启用可为空引用类型的代码中。我希望它能够区分 MyFunc<object>()
和 MyFunc<object?>()
情况,因此在前一种情况下处理空值时它会抛出,但在后一种情况下不会。
很明显我无法通过类型检查来检查这一点,因为引用类型可空性是一个编译时特性。
我该怎么做?有可能吗?
更新
看来我的意图不明确,所以我会详细说明。
我有一些生成值的代码。有时它可能会产生空值。我想让我的代码的用户能够决定他是否想从我的代码中获取空值。
对于可为 null 的结构,它确实可以正常工作:
int noNullsForThisVariable = MyFunc<int>();
int? nullsAreOkHere = MyFunc<int?>();
我现在想为引用类型提供相同的行为。
可能理解有误
不可为 null 的引用类型,例如字符串(而不是字符串?)只存在于设计时。在运行时字符串是可以为空的,不管你是否只是写字符串而不是字符串?。澄清了这一点,问题可能就得到了回答。在可空性方面,c# 8 没有任何变化。
让我解释一下为什么您的方法根本不适用于引用类型。 (至少到 c# 8 为止。此行为可能会在未来的 c# 版本中改变)
因为引用类型在运行时总是可以为空,所以类型本身没有区别。
这可能就是你不能做的原因:typeof(string?)
您会收到如下错误:"typeof cannot be used for nullable reference types"
但这并不一定意味着编译不会添加可区分的属性或类似的东西。 (确实可以)
但我们可以说,如果两种类型的字符串的 GetType() 将产生相同的类型实例,则这根本不可能是不同的类型。
自己用下面的代码检查一下。
string nonNullableString = "3434";
string? nullableString = "3434";
var nnStringType = nonNullableString.GetType();
var stringType = nullableString.GetType();
var isEqual = nnStringType == stringType; // true
if(object.ReferenceEquals(nnStringType, stringType)) // true
Debug.Print("it's the same object");
可空字符串和不可空字符串属于同一类型。
我正在尝试编写一个行为不同的通用函数,具体取决于它是否使用允许空值的参数调用。 在 C#8 之前的世界中,我使用的是这样的东西:
T MyFunc<T>()
{
object value = SomeExternalFunctionCall();
if (value == null)
{
if (typeof(T).AcceptsNulls())
{
return null;
}
else
{
throw new Exception($"{typeof(T).Name} does not support nulls");
}
}
return (T)value;
}
其中 AcceptsNulls()
是这样的:
public static bool IsAcceptNulls(this Type type)
{
if (type.IsValueType)
{
if (Nullable.GetUnderlyingType(type) != null)
{
return true;
}
return false;
}
return true;
}
因此 MyFunc
对引用类型和值类型都能愉快地工作,并适当支持可为 null 的值类型。
但现在我想将其包含在启用可为空引用类型的代码中。我希望它能够区分 MyFunc<object>()
和 MyFunc<object?>()
情况,因此在前一种情况下处理空值时它会抛出,但在后一种情况下不会。
很明显我无法通过类型检查来检查这一点,因为引用类型可空性是一个编译时特性。
我该怎么做?有可能吗?
更新
看来我的意图不明确,所以我会详细说明。
我有一些生成值的代码。有时它可能会产生空值。我想让我的代码的用户能够决定他是否想从我的代码中获取空值。
对于可为 null 的结构,它确实可以正常工作:
int noNullsForThisVariable = MyFunc<int>();
int? nullsAreOkHere = MyFunc<int?>();
我现在想为引用类型提供相同的行为。
可能理解有误
不可为 null 的引用类型,例如字符串(而不是字符串?)只存在于设计时。在运行时字符串是可以为空的,不管你是否只是写字符串而不是字符串?。澄清了这一点,问题可能就得到了回答。在可空性方面,c# 8 没有任何变化。
让我解释一下为什么您的方法根本不适用于引用类型。 (至少到 c# 8 为止。此行为可能会在未来的 c# 版本中改变)
因为引用类型在运行时总是可以为空,所以类型本身没有区别。
这可能就是你不能做的原因:typeof(string?) 您会收到如下错误:"typeof cannot be used for nullable reference types"
但这并不一定意味着编译不会添加可区分的属性或类似的东西。 (确实可以)
但我们可以说,如果两种类型的字符串的 GetType() 将产生相同的类型实例,则这根本不可能是不同的类型。
自己用下面的代码检查一下。
string nonNullableString = "3434";
string? nullableString = "3434";
var nnStringType = nonNullableString.GetType();
var stringType = nullableString.GetType();
var isEqual = nnStringType == stringType; // true
if(object.ReferenceEquals(nnStringType, stringType)) // true
Debug.Print("it's the same object");
可空字符串和不可空字符串属于同一类型。