未解析动态泛型类型的方法
我有这些类型:
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: Best Heavyed method match for 'GenericDAO
var obj = new Attachment() { /* set properties */ };
obj.Save();
我已经在 DomainObject.Save() "this" 中验证了这一点绝对是附件,因此该错误实际上没有意义。任何人都可以阐明为什么该方法无法解决吗?
更多信息 - 如果我将 DomainObject.Save() 的内容更改为使用反射,它就会成功:
public virtual void Save() {
var dao = Dao;
var type = dao.GetType();
var save = ((Type)type).GetMethod("Save");
save.Invoke(dao, new []{this});
}
I have these types:
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>(); } }
}
Then when I run this code it fails with RuntimeBinderException: Best overloaded method match for 'GenericDAO<Attachment>.Save(Attachment)' has some invalid arguments
var obj = new Attachment() { /* set properties */ };
obj.Save();
I've verified that in DomainObject.Save() "this" is definitely Attachment, so the error doesn't really make sense. Can anyone shed some light on why the method isn't resolving?
Some more information - It succeeds if I change the contents of DomainObject.Save() to use reflection:
public virtual void Save() {
var dao = Dao;
var type = dao.GetType();
var save = ((Type)type).GetMethod("Save");
save.Invoke(dao, new []{this});
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
问题在于动态方法调用的某些方面是在编译时解决的。这是设计使然。从语言规范(强调我的):
这里,组成表达式
this
具有编译时类型DomainObject
(简化:源代码位于泛型中类型,因此我们应该如何“查看”this 的编译时类型变得复杂,但希望我的意思能够被理解),并且由于 this 不是动态类型或类型参数, 其类型被视为其编译时类型。因此,绑定器会寻找一个方法
Save
,该方法采用DomainObject
类型的单个参数(或者向该方法传递类型的对象是合法的)编译时的 DomainObject
)。如果绑定发生在编译时,它看起来有点像这样:
但这行不通,因为
GenericDao
上唯一值得关注的候选方法是Attachment Save(Attachment)
,并且对于此方法,不存在从参数类型 (DomainObject
) 到参数类型 (附件
)。因此,我们得到了编译时错误:
this 是延迟到
dynamic
版本运行时的错误。反射不存在同样的问题,因为它不会尝试在编译时提取有关方法调用的“部分”信息,这与动态
版本不同。幸运的是,解决方法很简单,推迟对成分表达式类型的求值:
这使我们进入选项 1(编译时类型
dynamic
)。 成分表达式的类型被推迟到运行时,这有助于我们绑定到正确的方法。那么静态绑定的等效代码类似于:应该可以正常工作。
The problem is that some aspects of the dynamic method-call are resolved at compile-time. This is by design. From the language specification (emphasis mine):
Here, the constituent expression
this
has a compile-time typeDomainObject<int>
(simplification: the source-code is in a generic type, so that complicates how we should "view" the compile-time type ofthis
, but hopefully, what I mean is understood), and since this is not of type dynamic or a type-parameter, its type is taken as its compile-time type.So the binder looks for a method
Save
taking a single parameter of typeDomainObject<int>
(or to which it would have been legal to pass an object of typeDomainObject<int>
at compile-time).It would have looked somewhat like this had the binding happened at compile-time:
But this can't work since the only candidate-method of concern on
GenericDao<Attachment>
isAttachment Save(Attachment)
, and for this method, no implicit conversion exists from type of the argument (DomainObject<int>
) to the type of the parameter (Attachment
).So we get the compile-time error:
And this is the error that is deferred until run-time with the
dynamic
version. Reflection doesn't have the same problem because it doesn't attempt to extract "partial" information about the method-call at compile-time, unlike thedynamic
version.Fortunately, the fix is simple, defer the evaluation of the type of the constituent-expression:
This moves us into option 1 (compile-time type
dynamic
). The type of the constituent-expression is deferred until run-time, and this helps us bind to the right method. Then the statically-bound equivalent of the code is something like:which should work fine.
阿尼的回答很好;阿尼要求我自行决定添加一些额外的解释性文字,所以就这样吧。
基本上,这里发生的是动态调用,其中一些参数不是动态的,导致动态分析遵循已知的编译时信息。如果没有例子,这可能还不清楚。考虑以下重载:
会发生什么?我们必须进行动态调用,因此 ddd 的运行时类型用于进行重载决策。但 aaa 不是动态的,因此它的编译时类型用于进行重载决策。在运行时,分析器尝试解决这个问题:
并选择第二重载。它没有说“好吧,在运行时 aaa 是 Giraffe,因此我应该解决问题:
并选择第三个重载。
同样的事情也发生在这里。当你说
dao 的编译时类型是“动态”时,但是“this”的编译时类型不是。因此,在运行时解决重载解析问题时,我们使用“dao”的运行时类型,但编译器会为此给出错误。类型的组合,因此运行时绑定器也是如此。请记住,运行时绑定器的工作是告诉您如果编译器拥有有关标记为“动态”的所有内容的所有可用信息,它会说什么。运行时绑定器的工作不是改变 C# 的语义以使 C# 成为动态类型语言。
Ani's answer is quite fine; Ani asked me to add some extra explanatory text at my discretion, so here you go.
Basically what's going on here is that a dynamic invocation where some of the arguments are not dynamic causes the dynamic analysis to honour the compile-time information that was known. That's maybe not clear without an example. Consider the following overloads:
What happens? We have to do a dynamic invocation, so the runtime type of ddd is used to do overload resolution. But aaa is not dynamic, so its compile time type is used to do overload resolution. At runtime the analyzer tries to solve this problem:
and chooses the second overload. It does not say "well, at runtime aaa is Giraffe, therefore I should be solving the problem:
and choose the third overload.
The same thing is happening here. When you say
the compile-time type of dao is "dynamic", but the compile-time type of "this" is not. Therefore when solving the overload resolution problem at runtime, we use the runtime type of "dao" but the compile-time type of the argument. The compiler would have given an error for that combination of types, and therefore so does the runtime binder. Remember, the runtime binder's job is to tell you what would the compiler have said if it had all the information available about everything that was marked 'dynamic'. The runtime binder's job is not to change the semantics of C# to make C# a dynamically typed language.
这里已经有很好的答案了。我遇到了同样的问题。你说得对,反思确实有效。因为您通过调用 GetType() 来指定类型,该类型会在运行时解析。
或者我们可以使用动力学,如下所示
所以在你的情况下
这应该可行。
Quite good answers here already. I ran into the same problem. You're right reflection does work. Because you are specifying the type by saying GetType() which gets resolved at runtime.
Or we can use dynamics as follows
So in your case
This should work.
代码很混乱。我在这里看到两种可能的选择。
Dao 实际上是 GenericDao 的父级,因为否则您的 getter 类型不匹配:
或者,Dao 可能是 GenericDAO 的子级。但在这种情况下,吸气剂也不正确,而且情况实际上更糟。
所以最重要的是你的类/接口层次结构有问题。请澄清,我将相应地更新我的答案。
The code is confusing. I see two possible options here.
Dao is actually the parent of GenericDao because otherwise your getter has type mismatch:
Alternatively, Dao may be a child of GenericDAO. But in this case the getter is not correct either and the situation is actually worse.
So the bottom line is that there is a problem with your class/interface hierarchy. Please clarify and I will update my answer accordingly.