让我们从一段非常简单的代码开始:
decimal d = 2;
Console.WriteLine("d == 2 = {0}", d == 2);
Console.WriteLine("d == (decimal)2 = {0}", d == (decimal)2);
Console.WriteLine("d.Equals(2) = {0}", d.Equals(2));
Console.WriteLine("d.Equals((decimal)2) = {0}", d.Equals((decimal)2));
结果是4xtrue。现在,让我们将变量 d 的类型更改为 decimal?:
decimal? d = 2;
这次结果将是 True,True,False,True 。对这种情况的解释非常简单。对于 Nullable< T> 类型, Equals 方法实现如下:
public override bool Equals(object other)
{
if (!this.HasValue)
{
return (other == null);
}
if (other == null)
{
return false;
}
return this.value.Equals(other);
}
如果此具有值且其他参数不为null,则将调用 Decimal.Equals(对象值)。 Decimal.Equals(object value)方法以这种方式工作,如果 value 参数不是 decimal ,那么结果将始终为假
在我看来,目前的实现并不直观,我想知道为什么 Nullable&lt; T&gt; 不为开发人员提供 Equals 方法的通用版本,例如:< / p>
public bool Equals(T other)
{
if (!this.HasValue)
return false;
return this.value.Equals(other);
}
是故意还是遗漏?
评论1:
简要说明要明确。我建议 Nullable&lt; T&gt; 应该有两个 Equals 方法,即: public override bool Equals(object other)和 public bool Equals (其他)
答案 0 :(得分:3)
您可以编写(decimal)2
(将使用以下内容),而不是撰写2m
。
使用==
运算符时,不会发生装箱。 C#编译器将选择operator ==
匹配最佳的预定义重载(即C#语言规范中定义的重载;这不一定是真正的.NET方法)。
有重载:
operator ==(int x, int y);
operator ==(decimal x, decimal y);
没有像operator ==(decimal x, int y);
这样的“混合”重载。由于存在从int
到decimal
的隐式转换,因此当您使用2
时,您的文字2m
会隐式转换为==
。
使用Equals
,在某些情况下会发生拳击。你不需要那些无法控制的东西。举例来说,所有这些调用:
object.Equals(2, 2m);
object.Equals(2m, 2);
((object)2).Equals(2m);
((object)2m).Equals(2);
(2).Equals((object)2m);
(2m).Equals((object)2);
(2).Equals(2m);
返回false
!类型为Int32
的“两个”不等于Decimal
类型的“两个”。
只有当方法重载导致int
和decimal
之间的转换时,结果才会为true
。例如:
(2m).Equals(2); // true
因此,当Equals
的额外重载可以添加到Nullable<>
时,您描述的行为与Nullable<>
实际上并不相关。
答案 1 :(得分:1)
虽然我喜欢这些问题,但他们只能由负责该类型的设计团队的人员来回答。一个明显的解决方法是访问Value
T
并使用Equals
。{/ p>
我最好的猜测是,它可能会强制所有T
为IEquatable<T>
,以便在给定类型上一般访问Equals<T>
。这适用于核心值类型,但其他.NET结构不一定实现该接口,而enum
类型则不一定。
我认为可以通过类型检查/转换来完成,但这会产生大量的工作,而调用者只需执行:myNullable.GetValueOrDefault().Equals()
。
你可以创建一个扩展方法来完成这个任务,让它调用你需要显式指定泛型参数的方法(否则编译器会调用Equals(object)
:
class Program
{
static void Main(string[] args)
{
double? d = null;
Console.WriteLine(d.Equals<double>(0.0));
d = 0.0;
Console.WriteLine(d.Equals<double>(0.0));
Console.Read();
}
}
public static class NullableExtensions
{
public static bool Equals<T>(this T? left, T right) where T : struct, IEquatable<T>
{
if (!left.HasValue)
return false;
return right.Equals(left.Value);
}
}
<子> I've asked a question about why this call is needed. 子>
如果不存在合适的方法,原因是由于编译器实现仅使用扩展方法,因此必须强制它进入扩展方法,在这种情况下,Equals(object)
被认为比{{1}更合适因为后者是一种扩展方法。它在规范中。