是否可以在具有运算符重载的结构上抑制从 null 的隐式转换?
Is it possible to suppress implicit conversion from null on a struct with operator overloads?
通常,对于值(结构)类型,与 null(或对象类型)进行比较会导致编译器错误。
struct Value
{
}
class Program
{
static void Main()
{
object o = new object();
Value v = default;
// Error CS0019 Operator '==' cannot be applied to operands of type 'Value' and '<null>'
var a = v == null;
// Error CS0019 Operator '==' cannot be applied to operands of type 'Value' and 'object'
var b = v == o;
}
}
但是,如果我在结构上添加相等运算符重载,与 null 的比较将不再产生编译器错误:
struct Value
{
public static bool operator ==(Value l, Value r)
{
return true;
}
public static bool operator !=(Value l, Value r)
{
return true;
}
}
class Program
{
static void Main()
{
object o = new object();
Value v = default;
// compiler is now happy with this.
var a = v == null;
// Error CS0019 Operator '==' cannot be applied to operands of type 'Value' and 'object'
var b = v == o;
}
}
我相信这与向 Nullable<Value>
的隐式转换有关,但我记不起细节了。问题是:是否可以在保留编译器错误的同时在结构上重载这些运算符?
我重构了一些代码,我认为由于这个问题,代码库中有一些地雷。我也担心以后的代码可能会不小心写成这种形式,我真的很希望它产生一个编译器错误(而不必实现分析器)。
这是因为编译器会自动生成所谓的提升运算符,这些运算符也适用于可空值类型,您可以在 docs 中阅读。因此,对于给定的比较运算符 T, U -> bool
,其中 T
和 U
是不可为 null 的值类型,还存在提升运算符 T?, U -> bool
、T, U? -> bool
和 T?, U? -> bool
.
要抑制这些运算符,您可以显式定义它们并使用 Obsolete
属性进行修饰,并将 error
参数设置为 true
,如下所示:
struct Value
{
public static bool operator ==(Value l, Value r)
{
return true;
}
public static bool operator !=(Value l, Value r)
{
return true;
}
[Obsolete("Some error message", error: true)]
public static bool operator ==(Value? l, Value r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator ==(Value l, Value? r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator ==(Value? l, Value? r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator !=(Value? l, Value r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator !=(Value l, Value? r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator !=(Value? l, Value? r) =>
throw new NotImplementedException();
}
现在在像 new Value() == null
和 new Value() == (Value?)null
这样的比较中,将选择上面匹配的用户定义的运算符,因为它更具体并且会给出这样的错误:
error CS0619: 'Value.operator ==(Value, Value?)' is obsolete: 'Some error message'
通常,对于值(结构)类型,与 null(或对象类型)进行比较会导致编译器错误。
struct Value
{
}
class Program
{
static void Main()
{
object o = new object();
Value v = default;
// Error CS0019 Operator '==' cannot be applied to operands of type 'Value' and '<null>'
var a = v == null;
// Error CS0019 Operator '==' cannot be applied to operands of type 'Value' and 'object'
var b = v == o;
}
}
但是,如果我在结构上添加相等运算符重载,与 null 的比较将不再产生编译器错误:
struct Value
{
public static bool operator ==(Value l, Value r)
{
return true;
}
public static bool operator !=(Value l, Value r)
{
return true;
}
}
class Program
{
static void Main()
{
object o = new object();
Value v = default;
// compiler is now happy with this.
var a = v == null;
// Error CS0019 Operator '==' cannot be applied to operands of type 'Value' and 'object'
var b = v == o;
}
}
我相信这与向 Nullable<Value>
的隐式转换有关,但我记不起细节了。问题是:是否可以在保留编译器错误的同时在结构上重载这些运算符?
我重构了一些代码,我认为由于这个问题,代码库中有一些地雷。我也担心以后的代码可能会不小心写成这种形式,我真的很希望它产生一个编译器错误(而不必实现分析器)。
这是因为编译器会自动生成所谓的提升运算符,这些运算符也适用于可空值类型,您可以在 docs 中阅读。因此,对于给定的比较运算符 T, U -> bool
,其中 T
和 U
是不可为 null 的值类型,还存在提升运算符 T?, U -> bool
、T, U? -> bool
和 T?, U? -> bool
.
要抑制这些运算符,您可以显式定义它们并使用 Obsolete
属性进行修饰,并将 error
参数设置为 true
,如下所示:
struct Value
{
public static bool operator ==(Value l, Value r)
{
return true;
}
public static bool operator !=(Value l, Value r)
{
return true;
}
[Obsolete("Some error message", error: true)]
public static bool operator ==(Value? l, Value r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator ==(Value l, Value? r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator ==(Value? l, Value? r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator !=(Value? l, Value r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator !=(Value l, Value? r) =>
throw new NotImplementedException();
[Obsolete("Some error message", error: true)]
public static bool operator !=(Value? l, Value? r) =>
throw new NotImplementedException();
}
现在在像 new Value() == null
和 new Value() == (Value?)null
这样的比较中,将选择上面匹配的用户定义的运算符,因为它更具体并且会给出这样的错误:
error CS0619: 'Value.operator ==(Value, Value?)' is obsolete: 'Some error message'