我如何告诉编译器我的可空 C# 泛型约束?
How do I tell the compiler about my nullable c# generic constraints?
我有一个编辑器,可以让用户编辑对象(int、string、DateTime 等)的“简单”属性,因此它会遍历这些属性,然后为每个简单的 属性 支持编辑的对象:
#nullable enable
public class DataNode<T>
{
protected PropertyInfo Prop;
protected object Source;
protected T Value;
protected bool Modified;
public DataNode(PropertyInfo prop, object source)
{
Prop = prop;
Source = source;
object? value = prop.GetValue(source);
if (value is T t)
Value = t;
else
Value = default;
Modified = false;
}
public virtual bool IsValid()
{
return true;
}
public void Save()
{
if (Modified)
{
Prop.SetValue(Source, Value);
Modified = false;
}
}
}
然后我有特定属性的 subclasses - 例如,如果有一个 int 属性 我不希望用户能够输入负值,我可以创建一个特定的子class:
public class NonNegativeIntNode : DataNode<int>
{
public NonNegativeIntNode (PropertyInfo prop, object source)
: base(prop, source)
{
}
public override bool IsValid()
{
return Value >= 0;
}
}
所以一切正常,在你问之前:
- 选择使用哪个 DataNode class 由属性上的自定义属性控制
- DataNode 的功能远不止于此,为简单起见我省略了这些。例如 BoolDataNode class 知道要编辑这个值,它应该使用复选框
问题是编译器抱怨“Value = default;”是一个可能的空引用赋值,并且 DataNode 构造函数可能以 'Value'.
的空值退出
我可以通过将 Value 定义为“protected T? Value”来让编译器满意,但这会在其他地方增加不必要的复杂性来检查 null 值,因为我非常清楚 BoolDataNode 内部的 Value 永远不会为 null。
我尝试将 DataNode 拆分为两个 classes - NullableDataNode 和 NonNullableDataNode - 但在 NonNullableDataNode 内部,即使我指定了“where T: notnull”,编译器仍然担心 'default'
这似乎是在说“where T: notnull”的意思是“我永远不会将 Value 设置为 null”,而我想告诉编译器的是“T 是一个不能为 null 的类型”(比如 bool)
有没有一种方法可以让编译器确信一切正常,而无需简单地使用 pragma 关闭所有可空性警告?
I can keep the compiler happy by defining Value as "protected T? Value" but that adds unnecessary complication elsewhere to check for a null value when I know darn well that inside BoolDataNode that Value will never be null.
正确的做法是将其设为 protected T? Value
字段,因为如果 T
是引用类型,您将分配 null
。访问您的 Value
字段的人需要知道,如果 T
是 non-nullable 引用类型,他们可能仍会得到 null
。
当 T
不受约束时,您对 T?
的含义感到困惑。 T?
表示该值是“可默认的”,而不是“可空的”。这是一个细微的差别,但意味着您可以分配 default(T)
的值。另一种表达方式是:
If T
is a reference type, T?
means that you can assign null
. If T
is a value type, the ?
in T?
effectively has no meaning.
也就是说,DataNode<string>.Value
是string?
类型,而DataNode<bool>.Value
是bool
类型,而不是bool?
。
(从技术上讲,当 T
不受约束并且是值类型时,没有办法 T?
,意思是 Nullable<T>
。对于可为空的值类型,编译器输出一个成员类型 Nullable<T>
而不是 T
。但是,泛型由运行时而非编译器扩展。)
请注意,如果 T
被限制为值类型,这会发生变化:在这种情况下,T?
突然开始表示 Nullable<T>
。
我有一个编辑器,可以让用户编辑对象(int、string、DateTime 等)的“简单”属性,因此它会遍历这些属性,然后为每个简单的 属性 支持编辑的对象:
#nullable enable
public class DataNode<T>
{
protected PropertyInfo Prop;
protected object Source;
protected T Value;
protected bool Modified;
public DataNode(PropertyInfo prop, object source)
{
Prop = prop;
Source = source;
object? value = prop.GetValue(source);
if (value is T t)
Value = t;
else
Value = default;
Modified = false;
}
public virtual bool IsValid()
{
return true;
}
public void Save()
{
if (Modified)
{
Prop.SetValue(Source, Value);
Modified = false;
}
}
}
然后我有特定属性的 subclasses - 例如,如果有一个 int 属性 我不希望用户能够输入负值,我可以创建一个特定的子class:
public class NonNegativeIntNode : DataNode<int>
{
public NonNegativeIntNode (PropertyInfo prop, object source)
: base(prop, source)
{
}
public override bool IsValid()
{
return Value >= 0;
}
}
所以一切正常,在你问之前:
- 选择使用哪个 DataNode class 由属性上的自定义属性控制
- DataNode 的功能远不止于此,为简单起见我省略了这些。例如 BoolDataNode class 知道要编辑这个值,它应该使用复选框
问题是编译器抱怨“Value = default;”是一个可能的空引用赋值,并且 DataNode 构造函数可能以 'Value'.
的空值退出我可以通过将 Value 定义为“protected T? Value”来让编译器满意,但这会在其他地方增加不必要的复杂性来检查 null 值,因为我非常清楚 BoolDataNode 内部的 Value 永远不会为 null。
我尝试将 DataNode 拆分为两个 classes - NullableDataNode 和 NonNullableDataNode - 但在 NonNullableDataNode 内部,即使我指定了“where T: notnull”,编译器仍然担心 'default'
这似乎是在说“where T: notnull”的意思是“我永远不会将 Value 设置为 null”,而我想告诉编译器的是“T 是一个不能为 null 的类型”(比如 bool)
有没有一种方法可以让编译器确信一切正常,而无需简单地使用 pragma 关闭所有可空性警告?
I can keep the compiler happy by defining Value as "protected T? Value" but that adds unnecessary complication elsewhere to check for a null value when I know darn well that inside BoolDataNode that Value will never be null.
正确的做法是将其设为 protected T? Value
字段,因为如果 T
是引用类型,您将分配 null
。访问您的 Value
字段的人需要知道,如果 T
是 non-nullable 引用类型,他们可能仍会得到 null
。
当 T
不受约束时,您对 T?
的含义感到困惑。 T?
表示该值是“可默认的”,而不是“可空的”。这是一个细微的差别,但意味着您可以分配 default(T)
的值。另一种表达方式是:
If
T
is a reference type,T?
means that you can assignnull
. IfT
is a value type, the?
inT?
effectively has no meaning.
也就是说,DataNode<string>.Value
是string?
类型,而DataNode<bool>.Value
是bool
类型,而不是bool?
。
(从技术上讲,当 T
不受约束并且是值类型时,没有办法 T?
,意思是 Nullable<T>
。对于可为空的值类型,编译器输出一个成员类型 Nullable<T>
而不是 T
。但是,泛型由运行时而非编译器扩展。)
请注意,如果 T
被限制为值类型,这会发生变化:在这种情况下,T?
突然开始表示 Nullable<T>
。