Digital Nirvana:一个不存在的方法的callvirt最终会在哪里?

时间:2013-01-23 05:53:58

标签: c# .net polymorphism

我在库类上调用属性set-accessor,在其基类中将其标记为抽象。现在在运行时I force应用程序运行另一个版本的库,其中类只实现基类的底层接口,但不是从它派生的。

有趣的是,.NET将运行代码,但设置属性不起作用。幕后发生了什么?

违规代码:

MyDbParameter param = new MyDbParameter();
param.ParameterName = "p";
Console.Out.WriteLine("ParameterName: " + param.ParameterName);

图书馆2.0 (已编译)

public sealed class MyDbParameter : System.Data.Common.DbParameter
{
    public override string ParameterName
    {
       get { return _name; }
       set { _name = value; }
    }
    //...
}

图书馆1.0 (运行)

public sealed class MyDbParameter : MarshalByRefObject, IDbDataParameter, IDataParameter
{
    public string ParameterName
    {
        get { return _name; }
        set { _name = value; }
    }
    //...
}

查看调用代码的MSIL,我认为虚拟调用是通过基类的MetodTable解决的:

IL_0001: newobj instance void [Library]Library.MyDbParameter::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "p"
IL_000d: callvirt instance void [System.Data]System.Data.Common.DbParameter::set_ParameterName(string)

但运行代码时基类不存在 - DbParameter.set_ParameterName()也不存在。 .NET怎么可能不抱怨呢?实际上调用了哪种方法?


更新

根据Samuel的建议,我已经反编译了System.Data.Common.DbParameter类并在我的两个库中采用了它。无论我从MarshalByRefObject派生出来还是对所有内容进行评论,这种行为都会重现 - 我相信我已经伪造Mason's回答。

但是在这个过程中我发现了会发生什么:实际上它是库1中MyDbParameter的某些其他属性的setter / getter,例如, Size(类型为int!) - 它取决于代码中的属性顺序。在前一种情况下,我实现了其他属性的setter,因此我忽略了提供的值,因此我看不到任何效果。现在,如果所有这些都有自动getter / setter,我的代码输出实际上是正确的。

问题仍然存在:为什么.NET不会在运行时抱怨丢失的方法?

1 个答案:

答案 0 :(得分:3)

我相信你的必杀技充满了MarshalByRefObjects。它们由框架唯一处理,因为它可以对它们进行所有访问,以便将它们视为远程对象的代理。 MBRO实际上无法满足违规代码的请求(因为你的v1类不支持DbParameter::set_ParameterName),所以它走的是漫长的道路。它不被视为MissingMethodException,因为MBRO通常缺少请求的成员,因此运行时间要宽松得多。

但是,如果您在设置属性之前尝试更改违规代码以将参数强制转换为IDbDataParameter,我想它会起作用,因为v1和v2都支持该接口。