我看到一些非常奇怪的东西,我无法解释.我猜测我不熟悉的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; }
有趣的是,当将表达式转换为对象时,正确评估比较:
null == (object)context.GetObject<HistoryMessage>(id) true null != (object)context.GetObject<HistoryMessage>(id) false
没有平等的运算符覆盖.
编辑:事实证明有一个操作符超载,这是不正确的.但是,为什么在内部方法通用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,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; } //... }
这里发生了什么?
解决方法
>如你已经澄清的那样,==操作符失败,因为您的重载不正确.
>当转换为对象时,==操作符正常工作,因为它是被使用的对象的==的实现,而不是EquatableIdentifiableObject.
>在方法GetObject中,操作符正确地求值,因为它不是EquatableIdentifiableObject的正在使用的==的实现.在C#泛型中,运行时解析(至少在这里是相关的),而不是在编译时解决.注意==是静态而不是虚拟的.所以类型T在运行时被解析,但是调用==必须在编译时解决.在编译时,当编译器解析==它不会知道使用EquatableIdentifiableObject的==的实现.因为类型T有这个约束:其中T:DBObject,new(),DBObject的实现(如果有的话)将被使用.如果DBObject没有定义==,那么将使用第一个基类的实现(直到对象).
>当转换为对象时,==操作符正常工作,因为它是被使用的对象的==的实现,而不是EquatableIdentifiableObject.
>在方法GetObject中,操作符正确地求值,因为它不是EquatableIdentifiableObject的正在使用的==的实现.在C#泛型中,运行时解析(至少在这里是相关的),而不是在编译时解决.注意==是静态而不是虚拟的.所以类型T在运行时被解析,但是调用==必须在编译时解决.在编译时,当编译器解析==它不会知道使用EquatableIdentifiableObject的==的实现.因为类型T有这个约束:其中T:DBObject,new(),DBObject的实现(如果有的话)将被使用.如果DBObject没有定义==,那么将使用第一个基类的实现(直到对象).
有关EquatableIdentifiableObject的实现的更多评论==:
>您可以替换此部分:
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.如果==是一个实例方法就可以了.由于它是一种静态方法,所以名字自身有点误导.更好的名称将是o1和o2或沿着这条线的东西,以便两个操作数在更平等的基础上对待.