为什么没有Nullable <t> .Equals(T值)方法?</t>

时间:2013-09-11 07:44:11

标签: c# .net nullable

让我们从一段非常简单的代码开始:

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&lt; T&gt; 类型, 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 (其他)

2 个答案:

答案 0 :(得分:3)

您可以编写(decimal)2(将使用以下内容),而不是撰写2m

使用==运算符时,不会发生装箱。 C#编译器将选择operator ==匹配最佳的预定义重载(即C#语言规范中定义的重载;这不一定是真正的.NET方法)。

有重载:

operator ==(int x, int y);
operator ==(decimal x, decimal y);

没有像operator ==(decimal x, int y);这样的“混合”重载。由于存在从intdecimal的隐式转换,因此当您使用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类型的“两个”。

只有当方法重载导致intdecimal之间的转换时,结果才会为true。例如:

(2m).Equals(2);  // true

因此,当Equals 的额外重载可以添加到Nullable<>时,您描述的行为与Nullable<>实际上并不相关。

答案 1 :(得分:1)

虽然我喜欢这些问题,但他们只能由负责该类型的设计团队的人员来回答。一个明显的解决方法是访问Value T并使用Equals。{/ p>

我最好的猜测是,它可能会强制所有TIEquatable<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}更合适因为后者是一种扩展方法。它在规范中。