EquatableIdentifiableObject

EquatableIdentifiableObject

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

我有以下方法:

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

在测试我的应用程序时,我发现它的行为不正常-它为我知道数据库中不存在的对象返回true。因此,我停止了该方法,并立即执行了以下操作:
context.GetObject<HistoryMessage>(id)
null
null == context.GetObject<HistoryMessage>(id)
true
null != context.GetObject<HistoryMessage>(id)
true
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;
}

有趣的是,将表达式转换为object时,比较的结果正确:
null == (object)context.GetObject<HistoryMessage>(id)
true
null != (object)context.GetObject<HistoryMessage>(id)
false

没有等于运算符的替代。

编辑:原来有一个运算符(operator)过载,这是不正确的。但是,为什么在内部方法泛型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; }

    //...
}

这里发生了什么?

最佳答案

  • 正如您已经阐明的那样,==运算符对于您的类型失败,因为您有不正确的重载。
  • 强制转换为对象时,==运算符可以正常工作,因为它使用的是object's==实现,而不是EquatableIdentifiableObject's
  • GetObject方法中,运算符(operator)正确评估,因为它不是正在使用的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);
    }
    



    和:
    // If both are null, or both are the same instance, return true.
    if (object.ReferenceEquals(h1, h2))
    {
        return true;
    }
    
  • 替换
  • 会更强大

    public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
    {
        ...
    }
    



    和:
    public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
    {
        return !(self == other);
    }
    
  • ==定义签名的方式有些误导。第一个参数名为self,第二个参数名为other。如果==是一个实例方法,那没关系。由于它是静态方法,因此名称self有点误导。更好的名称是o1o2或类似的名称,以便使两个操作数在更平等的基础上对待。
  • 10-08 18:32