11

我在一个库类上调用一个属性集访问器,它在它的基类中被标记为抽象。现在在运行时,我强制应用程序针对库的另一个版本运行,其中该类仅实现基类的底层接口,但不是从它派生的。

有趣的是,.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或将其全部评论出来,这种行为都会重现 - 在此我相信我已经伪造了梅森的回答。

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

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

4

1 回答 1

3

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

但是,如果您IDbDataParameter在设置属性之前尝试将有问题的代码更改为将 param 转换为 an,我想它会起作用,因为 v1 和 v2 都支持该接口。

于 2013-01-25T21:20:40.553 回答