对于expr == null和expr!= null,对null的比较计算结果为true

Leo*_*ica 10 .net c# null

我看到一些非常奇怪的东西,我无法解释.我猜测一些我不熟悉的C#边缘情况,或运行时/发射器中的错误?

我有以下方法:

public static bool HistoryMessageExists(DBContext context, string id)
{
    return null != context.GetObject<HistoryMessage>(id);
}
Run Code Online (Sandbox Code Playgroud)

在测试我的应用程序时,我发现它是行为不端的 - 它正在返回true我知道在我的数据库中不存在的对象.所以我停在了方法上,在立即,我运行了以下内容:

context.GetObject<HistoryMessage>(id)
null
null == context.GetObject<HistoryMessage>(id)
true
null != context.GetObject<HistoryMessage>(id)
true
Run Code Online (Sandbox Code Playgroud)

GetObject 定义如下:

public T GetObject<T>(object pk) where T : DBObject, new()
{
    T rv = Connection.Get<T>(pk);

    if (rv != null)
    {
        rv.AttachToContext(this);
        rv.IsInserted = true;
    }

    return rv;
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,在将表达式转换object为时,正确评估了比较:

null == (object)context.GetObject<HistoryMessage>(id)
true
null != (object)context.GetObject<HistoryMessage>(id)
false
Run Code Online (Sandbox Code Playgroud)

没有相等运算符覆盖.

编辑:事实证明有一个操作员过载,这是不正确的.但是为什么在内部方法generic中正确评估等式GetObject,在这种情况下rv类型HistoryMessage是什么.

public class HistoryMessage : EquatableIdentifiableObject
{
    public static bool HistoryMessageExists(DBContext context, string id)
    {
        var rv = context.GetObject<HistoryMessage>(id);
        bool b = rv != null;
        return b;
    }

    public static void AddHistoryMessage(DBContext context, string id)
    {
        context.InsertObject(new HistoryMessage { Id = id });
    }
}

public abstract partial class EquatableIdentifiableObject : DBObject, IObservableObject
{
    public event PropertyChangedEventHandler PropertyChanged;

    [PrimaryKey]
    public string Id { get; set; }

    //...
}

public abstract partial class EquatableIdentifiableObject
{
    //...

    public static bool operator ==(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
    {
        if (ReferenceEquals(self, null))
        {
            return ReferenceEquals(other, null);
        }

        return self.Equals(other);
    }

    public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
    {
        if (ReferenceEquals(self, null))
        {
            return !ReferenceEquals(other, null);
        }

        return !self.Equals(other);
    }
}

public abstract class DBObject
{
    [Ignore]
    protected DBContext Context { get; set; }

    [Ignore]
    internal bool IsInserted { get; set; }

    //...
}
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?

Lad*_*adi 3

  • 正如您已经澄清的那样,==您的类型的运算符失败,因为您的重载不正确。
  • 当转换为对象时,==运算符工作正常,因为它是使用object's的实现==,而不是EquatableIdentifiableObject's
  • 在该方法中,GetObject操作员评估正确,因为它不是正在使用EquatableIdentifiableObject's的实现。==在 C# 中,泛型在运行时解析(至少在此处相关的意义上),而不是在编译时解析。请注意,这==是静态的而不是虚拟的。因此类型T在运行时解析,但调用==必须在编译时解析。在编译时,当编译器解析时,==它不会知道EquatableIdentifiableObject's使用==. 由于类型 T 有此约束:where T : DBObject, new(),因此DBObject's将使用实现(如果有)。如果DBObject未定义,则将使用==第一个执行此操作的基类(最多 )的实现。object

EquatableIdentifiableObject's关于实施的更多评论==

  • 您可以替换这部分:
if (ReferenceEquals(self, null))
{
     return ReferenceEquals(other, null);
}
Run Code Online (Sandbox Code Playgroud)

和:

// If both are null, or both are the same instance, return true.
if (object.ReferenceEquals(h1, h2))
{
    return true;
}
Run Code Online (Sandbox Code Playgroud)
  • 更换的话会更坚固
public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

和:

public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
{
    return !(self == other);
}
Run Code Online (Sandbox Code Playgroud)
  • 您定义签名的方式==有点误导。第一个参数名为named self,第二个参数名为named other==如果是实例方法那就没问题了。由于它是静态方法,因此名称self有点误导。更好的名称应该是o1ando2或类似的名称,以便两个操作数得到更平等的对待。