我有一些代码在我有通用约束时可以工作
where T : struct
:
public readonly struct AnnotatedValue<T> where T : struct
{
public AnnotatedValue(Object? value, ...)
{
Value = ConvertValue(value);
...
}
public T? Value { get; init; }
... more properties ...
private static T? ConvertValue(Object? value)
{
return value switch
{
null => null,
T castValue => castValue,
_ => (T?)Convert.ChangeType(value, typeof(T))
};
}
}
这里的总体目标是将非泛型
Object?
转换为请求的类型(很可能是数字、结构体或字符串);如果请求的类型是值类型,则结果应包装在 Nullable<T>
中以处理空输入,如果请求的类型是引用类型,则结果允许为空。
如果我删除
where T : struct
约束,则 null => null
行会导致编译器错误 CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
我不想使用
default(T)
,因为这可能会返回 0 值,而不是具有 null 值的 Nullable。
上下文是我在数据源 API 中调用一个非泛型方法,该方法返回
Object?
,其中返回值可能为 null、装箱值类型(数字或结构)或字符串。我可以访问指示实际类型的元数据,但此特定 API 将其返回为 Object?
;我的目标是灵活地将其返回为强类型表示。
如果请求的类型是值类型,我想将返回值转换为与请求的值类型对应的
Nullable<foo>
。但如果请求的类型是 String
,我想返回 String?
(这是一个可为空的引用类型,因此与 where T : struct
泛型约束不兼容)。
有没有一种方法可以实现这种灵活的转换,而不需要调用者根据所需类型是值类型还是引用类型来调用不同的方法?
如果我必须为值
where T : struct
和引用 where T : class
实现不同的泛型方法,是否有一种方法可以使用包装方法来检查运行时类型信息并调用一个方法或另一个方法来进行转换?
这一切都取决于在值类型情况下传递的内容。如果是
T
,那么:你什么也做不了。如果没有 int
约束,T : struct
并不意味着
T?
;它的意思是“
Nullable<T>
,顺便说一句,编译器应该从 NRT 的角度考虑这个可以为空”。如果 T
是 T
,那么它 已经是
int?
,并且 Nullable<T>
已经意味着“空”。最终default(T)
表达了两个相互竞争的事物,在这里你无法得到你想要的确切组合。特别是,您不能告诉编译器(或更具体地说,JIT)“如果
T?
恰好是值类型,请将
T
交换为 T
”。