C# 未解析动态泛型类型的方法

C# 未解析动态泛型类型的方法,c#,generics,dynamic,C#,Generics,Dynamic,我有以下几种: 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() {

我有以下几种:

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>(); } }
}
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
我已经验证过,在DomainObject.Save()中,“this”肯定是附件,所以这个错误没有意义。有人能解释一下为什么这个方法不能解决问题吗

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
更多信息-如果我将DomainObject.Save()的内容更改为使用反射,则会成功:

public virtual void Save() {
    var dao = Dao;
    var type = dao.GetType();
    var save = ((Type)type).GetMethod("Save");
    save.Invoke(dao, new []{this});
}
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument

代码令人困惑。我在这里看到两种可能的选择

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
Dao实际上是GenericDao的父级,因为否则getter的类型不匹配:

public class Dao 
{
    void Save();
}

public class GenericDao<T> : Dao
{
    public virtual T Save(T) {...}
}

// error here because GenericDao does not implement Dao.
protected dynamic Dao { get { return new GenericDAO<Attachment>(); } }
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
公共类Dao
{
作废保存();
}
公共类GenericDao:Dao
{
公共虚拟T保存(T){…}
}
//此处出错,因为GenericDao未实现Dao。
受保护的动态Dao{get{return new GenericDAO();}}
或者,Dao可能是GenericDAO的子级。但在这种情况下,getter也不正确,情况实际上更糟

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument

因此,底线是类/接口层次结构存在问题。请澄清,我将相应地更新我的答案。

问题在于动态方法调用的某些方面在编译时得到了解决。这是故意的。根据语言规范(重点):

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
7.2.3成分表达的类型

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
当一个操作被静态绑定时, 组成表达式的类型 (例如,一个接收者和一个论点 索引(或操作数)始终为 被认为是编译时类型 那句话的意思。手术时 是动态绑定的,类型为 组分表达式是确定的 以不同的方式取决于 组件的编译时类型 表达方式:

dao.Save((dynamic)this);
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
•成分表达 编译时类型是动态的 被认为具有 表达式的实际值 在运行时计算为

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
•A 成分表达式 编译时类型是一个类型参数 被认为具有以下类型: 类型参数已绑定到 运行时

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
否则,组成部分 表达式被认为有其自身的特性 编译时类型。

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
这里,组成表达式
this
有一个编译时类型
DomainObject
(简化:源代码是泛型类型,这使得我们应该如何“查看”this的编译时类型变得复杂,但希望我的意思是可以理解的),因为这不是动态类型或类型参数,它的类型作为编译时类型

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
因此,活页夹寻找一个方法
Save
,该方法使用
DomainObject
类型的单个参数(或者在编译时将
DomainObject
类型的对象传递给该参数是合法的)

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
如果绑定发生在编译时,看起来会有点像这样:

// 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);
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
这是延迟到运行时的
动态版本的错误。反射没有同样的问题,因为它不尝试在编译时提取有关方法调用的“部分”信息,这与
动态版本不同

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
幸运的是,修复方法很简单,推迟对组成表达式类型的计算:

dao.Save((dynamic)this);
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
这将使我们进入选项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); 
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
//添加了额外的强制转换,以便从泛型类型编译
附件o=(附件)(对象)本;
GenericDao=(GenericDao)dao;
//没问题,GenericDao上的Save方法
//接受类型为Attachment的单个参数。
dao.Save(o);

这应该行得通。

阿尼的回答很好;Ani要求我酌情添加一些额外的解释性文本,所以给你

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
基本上,这里发生的是,一些参数不是动态的动态调用会导致动态分析尊重已知的编译时信息。如果没有一个例子,这可能是不清楚的。考虑下面的重载:

static void M(Animal x, Animal y) {}
static void M(Animal x, Tiger y) {}
static void M(Giraffe x, Tiger y) {}
...
dynamic ddd = new Tiger();
Animal aaa = new Giraffe();
M(aaa, ddd);
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
会发生什么?我们必须进行动态调用,因此使用ddd的运行时类型进行重载解析。但aaa不是动态的,所以它的编译时类型用于进行重载解析。在运行时,分析器尝试解决此问题:

M((Animal)aaa, (Tiger)ddd);
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
并选择第二个重载。它没有说“嗯,在运行时aaa是长颈鹿,因此我应该解决这个问题:

M((Giraffe)aaa, (Tiger)ddd);
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
然后选择第三个重载

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
同样的事情也发生在这里,当你说

dao.Save(this)
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument

dao的编译时类型是“动态的”,但“this”的编译时类型不是。因此,在解决运行时的过载解决问题时,我们使用“dao”的运行时类型但是参数的编译时类型。编译器可能会给出该类型组合的错误,因此运行时绑定器也会给出错误。请记住,运行时绑定器的任务是告诉您,如果编译器拥有所有标记为“动态”的信息,那么它会说些什么。运行时绑定器的工作不是改变C的语义,使C成为一种动态类型语言。

这里已经有了很好的答案。我遇到了同样的问题。你说得对,反射确实有效。因为您是通过说GetType()来指定类型的,GetType()在运行时得到解析

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
或者我们可以使用如下动力学

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
    dynamic d = this;
    dynamic e = @event;
    d.Apply(e);
那么你的情况呢

var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument
public abstract class DomainObject {
    // Some properties

    protected abstract dynamic Dao { get; }

    public virtual void Save() {
        var dao = Dao;
        dynamic d = this; //Forces type for Save() to be resolved at runtime
        dao.Save(d);
    }
}

这应该行得通。

对此进行调查。我不知道原因,所以我不会发布答案,但我认为问题在于
这个
。在您的虚拟
Save
方法中,我使用了它,添加了
dynamic obj=this;dao.Save(obj)并在运行时解析。测试它,看看它在给定代码的情况下是否真正起作用。@Ani,我将他的代码更新为可编译的代码,但仍然显示出他在运行时出现的错误行为。这是一件有趣的事
var method = ((object)this).GetType().GetMethod("Apply", new Type[] { @event.GetType() }); //Find the right method
            method.Invoke(this, new object[] { @event }); //invoke with the event as argument