I call a property set-accessor on a library class which in its base class is marked as abstract. Now at runtime I force the application to run against another version of the library where the class implements only the underlying interfaces of the base class, but isn't derived from it.
Interestingly, .NET will run the code, but setting the property has no effect. What's going on behind the scenes?
Offending Code:
MyDbParameter param = new MyDbParameter();
param.ParameterName = "p";
Console.Out.WriteLine("ParameterName: " + param.ParameterName);
Library 2.0 (compiled)
public sealed class MyDbParameter : System.Data.Common.DbParameter
{
public override string ParameterName
{
get { return _name; }
set { _name = value; }
}
//...
}
Library 1.0 (run)
public sealed class MyDbParameter : MarshalByRefObject, IDbDataParameter, IDataParameter
{
public string ParameterName
{
get { return _name; }
set { _name = value; }
}
//...
}
Looking at the MSIL of the calling code I suppose the virtual call is resolved via the MetodTable of the base class:
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)
But the base class does not exist when running the code - neither does DbParameter.set_ParameterName()
. How is it possible that .NET doesn't complain about that? Which method actually gets called?
UPDATE:
As suggested by Samuel I've decompiled the class System.Data.Common.DbParameter
and adopted it in both of my libraries. The behavior reproduces regardless of wherher I derive from MarshalByRefObject
or comment all of it out - hereby I believe I have falsified Mason's answer.
But during the process I've discovered what happens: In reality it is the setter/getter of some other property of MyDbParameter
in Library 1 what is called, e.g. Size
(which is of type int
!) - it depends on the property order in code. In the previous case I implemented the setters of other properties so that I ignored the supplied values, therefore I could see no effect. Now if all of them have automatic getters/setters, the output of my code is actually correct.
The question remains: Why doesn't .NET complain about the missing method at runtime?
I believe your nirvana is filled with MarshalByRefObjects
. They are handled uniquely by the framework, as it instruments all access to them in order to treat them as proxies for remote objects. The MBRO can't actually fulfill the offending code's request (since your v1 class doesn't support DbParameter::set_ParameterName
), so it is taking the long road to nowhere. It isn't treated as a MissingMethodException
since MBRO's are usually missing requested members, so the runtime is far more lenient.
However, if you tried changing your offending code to cast param as an IDbDataParameter
before setting the property, I imagine it would work, since both v1 and v2 support that interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With