8

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

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? 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);
}

如果this有一个值并且其他参数不为 null,则将调用Decimal.Equals(object value)Decimal.Equals(object value)方法以这种方式工作,如果value参数不是小数,则结果将始终为false

在我看来,当前的实现并不直观,我想知道为什么Nullable<T>不为开发人员提供Equals方法的通用版本,例如:

public bool Equals(T other)
{
    if (!this.HasValue)
        return false;

    return this.value.Equals(other);
}

是故意的还是疏忽了?

评论1:

简短的评论要清楚。我建议Nullable<T>应该有两个Equals方法,即:public override bool Equals(object other)public bool Equals(T other)

4

2 回答 2

3

(decimal)2你可以写而不是写2m(将在下面使用)。

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

有重载:

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

没有像operator ==(decimal x, int y);. 因为存在从intto的隐式转换,所以当您使用 时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

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

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

Equals 因此,虽然可以添加额外的重载Nullable<>,但您描述的行为与Nullable<>.

于 2013-09-11T10:31:55.133 回答
1

尽管我喜欢这些问题,但它们只能由负责该类型的设计团队的人员真正回答。一个明显的解决方法是访问Valuethat isT并使用 the Equalsof that。

我最好的猜测是,它可能会强制 all Tto be 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);
    }
}

我问了一个关于为什么需要这个电话的问题。

事实证明,必须将其强制转换为扩展方法的原因是编译器实现仅在不存在合适的方法时才使用扩展方法,在这种情况下Equals(object)被认为比后者更合适,Equals<T>(T)因为后者是扩展方法。它在规范中。

于 2013-09-11T07:55:20.077 回答