gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 04:10:27 25 4
gpt4 key购买 nike

在一个示例中,我的教授将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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com