public class GenericDao<T> { public T Save(T t) { return t; } } public abstract class DomainObject { // Some properties protected abstract dynamic Dao { get; } public virtual void Save() { var dao = Dao; dao.Save(this); } } public class Attachment : DomainObject { protected dynamic Dao { get { return new GenericDao<Attachment>(); } } }
然后当我运行这个代码,它失败与RuntimeBinderException:最佳重载方法匹配’GenericDAO< Attachment> .Save(附件)’有一些无效的参数
var obj = new Attachment() { /* set properties */ }; obj.Save();
我已经验证了在DomainObject.Save()“这”绝对是Attachment,所以错误并没有真正的意义.任何人都可以清楚,为什么方法不能解决?
一些更多的信息 – 如果将DomainObject.Save()的内容更改为使用反射,则会成功:
public virtual void Save() { var dao = Dao; var type = dao.GetType(); var save = ((Type)type).GetMethod("Save"); save.Invoke(dao,new []{this}); }
解决方法
7.2.3 Types of constituent expressions
When an operation is statically bound,
the type of a constituent expression
(e.g. a receiver,and argument,an
index or an operand) is always
considered to be the compile-time type
of that expression. When an operation
is dynamically bound,the type of a
constituent expression is determined
in different ways depending on the
compile-time type of the constituent
expression:• A constituent expression
of compile-time type dynamic is
considered to have the type of the
actual value that the expression
evaluates to at runtime• A
constituent expression whose
compile-time type is a type parameter
is considered to have the type which
the type parameter is bound to at
runtime• Otherwise the constituent
expression is considered to have its
compile-time type.
这里,组成的表达式具有编译时类型DomainObject< int> (简化:源代码是一种通用类型,因此我们应该如何“查看”这个编译时类型,但希望我的意思是被理解),因为这不是动态类型或type-parameter,其类型作为其编译时类型.
所以绑定器查找一个方法Save一个类型为DomainObject< int>的单个参数(或者在编译时传递类型为DomainObject< int>的对象是合法的).
看起来有点像这样在编译时发生了约束:
// Extra casts added to highlight the error at the correct location. // (This isn't *exactly* what happens.) DomainObject<int> o = (DomainObject<int>) (object)this; GenericDao<Attachment> dao = (GenericDao<Attachment>)Dao; // Compile-time error here. // A cast is attempted from DomainObject<int> -> Attachment. dao.Save(o);
但是,由于GenericDao< Attachment>所关心的唯一候选方法 – 是附件保存(附件),对于此方法,从参数类型(DomainObject< int>)到参数类型(Attachment)不存在隐式转换.
所以我们得到编译时错误:
The best overloaded method match for 'GenericDao<Attachment>.Save(Attachment)' has some invalid arguments Argument 1: cannot convert from 'DomainObject<int>' to 'Attachment'
而这是使用动态版本延迟到运行时的错误.反射没有相同的问题,因为它不会在编译时尝试提取与方法调用相关的“局部”信息,而不像动态版本.
幸运的是,修复很简单,推迟了组件表达式的评估:
dao.Save((dynamic)this);
这使我们进入选项1(编译时类型动态).构成表达式的类型推迟到运行时,这有助于我们绑定到正确的方法.那么代码的静态等价物就像:
// Extra casts added to get this to compile from a generic type Attachment o = (Attachment)(object)this; GenericDao<Attachment> dao = (GenericDao<Attachment>)Dao; // No problem,the Save method on GenericDao<Attachment> // takes a single parameter of type Attachment. dao.Save(o);
这应该工作正常.