c# - 'this'关键字是否可以等于null?

标签 c# .net oop equals

在一个示例中,我的教授将Equals实现如下:

public class Person {
    private string dni;

    // ...

    public override bool Equals(object o) {
        if (o == null)
            return (this == null);
        else {
            return ((o is Person) && (this.dni == (o as Person).dni));
        }
    }
}

我没有使用C#的经验,但是据我所知this在成员函数中不能为null(至少在C++和Java(我知道的语言)中是正确的),所以if似乎对我来说很奇怪。

我是对的还是我不知道C#中的任何组件会使测试this == null成为必需?

最佳答案

I have no experience with C#, but as far as I know this cannot be null inside a member function (at least this is true in C++ and Java, the languages I know)



首先,请注意您的陈述是错误的。

在C++中,在空接收器上调度方法是未定义的行为,并且未定义的行为意味着任何事情都可能发生。 “任何内容”都包括将NULL作为this传递并继续运行的程序,就好像没有什么错。当然,在C++中检查this是否为null有点愚蠢,因为只有在您不知道程序在做什么的情况下,检查才为true,因为它的行为是不确定的。

我不知道this在Java中是否可以为null。

现在解决您有关C#的问题。假设==没有重载。稍后我们将回到这一点。

您的方法是用C#编写的。假设使用空接收器从C#程序调用它。 C#编译器评估接收方是否可能为null。如果它可能为null,则确保它在调用该方法之前生成对代码进行空检查的代码。因此,在这种情况下,此检查毫无意义。当然,这是99.9999%的可能性。

假设它是通过反射调用的,就像Mike Z的回答一样。在这种情况下,不是C#语言执行调用;而是使用C#语言执行调用。相反,有人故意滥用反射。

假设它是从另一种语言调用的。我们有一个虚拟的方法;如果通过虚拟调度从另一种语言调用它,则必须执行空检查,因为否则我们怎么知道虚拟插槽中有什么?在那种情况下,它不能为null。

但是,假设它是使用非虚拟调度从另一种语言调用的。在这种情况下,另一种语言无需实现检查空值的C#功能。它可以调用它并传递null。

因此,在C#中可以通过多种方式将this用作null,但它们都已远远超出了主流。因此,人们很少像您的教授那样编写代码。 C#程序员习惯上认为this不是null,并且从不检查它。

现在我们已经解决了这个问题,让我们再批评一下该代码。
public override bool Equals(object o) {
    if (o == null)
        return (this == null);
    else {
        return ((o is Person) && (this.dni == (o as Person).dni));
    }
}

首先,有一个明显的错误。我们假设this可以为null,好的,让我们开始吧。 是什么阻止this.dni引发空引用异常??? 如果要假设this可以为null,那么至少要始终这样做! (在Coverity,我们将这种情况称为“前向空缺”。)

下一步:我们覆盖Equals,然后在内部使用==,大概是指引用相等。 这就是疯狂! 现在,我们遇到一种情况,其中x.Equals(y)可以为true,但是x==y可以为false!这太可怕了。请不要去那里。如果要覆盖Equals,则同时重载==,并在执行时实现IEquatable<T>

(现在,有一个合理的论点是,疯狂是朝着任一方向发展的;如果==与具有值语义的Equals一致,那么personx == persony可能不同于(object)personx == (object)persony,这似乎也很奇怪。这里的要点是,平等被搞砸了在C#中。)

此外:如果==稍后被覆盖怎么办?现在,当代码的作者明确希望进行引用比较时,Equals会调用重写的==运算符。这是错误的秘诀。

我的建议是:(1)写一个做正确的事情的静态方法,(2)每次可能对哪种含义表示混淆时都使用ReferenceEquals:
private static bool Equals(Person x, Person y)
{
    if (ReferenceEquals(x, y))
        return true;
    else if (ReferenceEquals(x, null))
        return false;
    else if (ReferenceEquals(y, null))
        return false;
    else 
        return x.dni == y.dni;
}

很好地涵盖了所有情况。 请注意,当引用相等语义表示时,对读者来说是非常清楚的。还要注意,此代码使调试各种可能性变得非常容易。最后,请注意,我们尽早采用了最便宜的产品。如果对象的引用相等,那么我们就不必对字段进行潜在的昂贵比较!

现在,其他方法很简单:
public static bool operator ==(Person x, Person y) 
{
  return Equals(x, y);
}
public static bool operator !=(Person x, Person y) 
{
  return !Equals(x, y);
}
public override bool Equals(object y)
{
  return Equals(this, y as Person);
}
public bool Equals(Person y)
{
  return Equals(this, y);
}

请注意,我的方式比您教授的方式更加优雅和清晰。并请注意,我的方法无需将this直接与null进行比较即可处理null this

再说一次:所有这些都说明折衷的立场已经到来,在其中值和引用相等都是可能的,并且即使没有假设==也有四种(!=object.Equals(object)IEquatable<T>.Equals(T)this)实现相等的方法非常复杂且令人困惑。可以或不能为null

如果您对此主题感兴趣,我将在本周的博客中描述一个稍微棘手的问题:如何实现一般的比较,包括不平等。

http://ericlippert.com/2013/10/07/math-from-scratch-part-six-comparisons/

作为对C#如何处理相等性的批评,这些注释特别有趣。

最后:不要忘记覆盖GetHashCodeMake sure you do it right.

关于c# - 'this'关键字是否可以等于null?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19300245/

相关文章:

java - 静态/编译时多态性

c++依赖注入(inject)+ Demeter法则+记录器/断言

c# - 将鼠标点击位置转换为给定比例

c# - WinForms 消息循环没有响应

c# - C++/CLI 使用抽象方法从 native C++ 类继承并将其公开给 C#

c# - 在 Azure 角色中使用 SmtpClient 时出现 "The function requested is not supported"异常

c# - 并行订购消耗品

c# - 当我在我的 WPF 窗口中放置一个文本框或按钮时,它是使用 DirectX 还是 User32 呈现的?

c# - 什么是数据表示控件的 ItemCommand 和 ItemDatabound?

php - 公共(public)属性无法访问面向对象的 PHP