.Net Binary Compatibility

Let's say we have a base interface defined in C # like:

interface IBase
{
    int Prop1 { get; set }
    string Prop2 { get; set }
}

      

Then we have a derived interface as follows:

interface ISub1: IBase
{
    int Prop3 { get; set }
}

      

These interfaces are defined in the API assembly from which custom applications are compiled and run. (The assembly also includes unaffected classes that implement these interfaces and the public factory methods for getting instances.) All existing code uses ISub1

, there is no existing code that directly links to IBase

. This was done in anticipation that we would eventually want to introduce the second derived interface ISub2

as a peer ISub1

, and that is now. Unfortunately, although we found that ISub2

we shouldn't contain Prop2

(just Prop1 and some additional unique properties), so we want to "downgrade" this property to ISub1

, resulting in the following revised interfaces:

interface IBase
{
    int Prop1 { get; set }
}

interface ISub1: IBase
{
    string Prop2 { get; set }
    int Prop3 { get; set }
}

interface ISub2: IBase
{
    string Prop4 { get; set }
}

      

Given that there are no consumers IBase

, it looks like we should be able to do this with impunity (and I'm sure we could do it in Java), but when trying to do this we ran into a binary compatibility issue with code compiled against the old interface definitions ... In particular:

ISub1 s1 = ... // get an instance
s1.Prop2 = "help";

      

This code, when run with a new interface definition, fails with an exception like this:

System.MissingMethodException: Method not found: 'Void MyNamespace.IBase.set_Prop2 (System.String).

Pay attention to the link to IBase

. I guess this is because the seeming call ISub1.set_Prop2

was hardcoded to where it was Prop2

actually put in IBase

.

Can anyone help me out of this puzzle? That is, is there a way to rephrase the interfaces so that the definition of ISub2 is "clean" (doesn't include the extraneous Prop2)? Asking to recompile all existing applications is out of the question.

+3


source to share


4 answers


By writing it to TryRoslyn , it becomes abundantly clear that there is a difference based on where you put the property in the interface:

Given:

interface ISub1A: IBaseA
{
    int Prop3 { get; set; }
}

interface IBaseA
{
    int Prop1 { get; set; }
    string Prop2 { get; set; }
}

interface ISub1B: IBaseB
{
    int Prop3 { get; set; }
    string Prop2 { get; set; }
}

interface IBaseB
{
    int Prop1 { get; set; }
}

      

and

ISub1A a = null;
a.Prop2 = "Hello";

ISub1B b = null;
b.Prop2 = "Hello";

      



(note that in both cases I am using the interface ISub1*

in C # code)

Generated IL Code:

IL_0001: ldstr "Hello"
IL_0006: callvirt instance void IBaseA::set_Prop2(string)
IL_000b: ldnull
IL_000c: ldstr "Hello"
IL_0011: callvirt instance void ISub1B::set_Prop2(string)

      

therefore the IL code "correctly" resolves the interface where the property is actually defined.

+1


source


Kind of hacks and not sure if it will work, but it might be worth a try



interface IBase0
{
    int Prop1 { get; set; }
}
interface IBase : IBase0
{
    int Prop1 { get; set; }
    string Prop2 { get; set; }
}
interface ISub1: IBase
{
    int Prop3 { get; set; }
}
interface ISub2 : IBase0
{
    int Prop4 { get; set; }
}

      

+1


source


Mainly:

interface ISub1: IBase

      

Simply says "any class that implements ISub1

promises to also implement IBase

". There is no mixing of methods defined in each interface, so this also means that " ISub1

contains 3 properties, Prop1

- Prop3

".

So why doesn't it work. ISub1

is currently defined to require exactly one property called Prop3

.

+1


source


First, you have to hide to ISub2.Prop2

implement it explicitly . Then, depending on why ISub2

you shouldn't contain Prop2

, you must either drop that implementation using the ObsoleteAttribute , or throw an InvalidOperationException on both accessories.

0


source







All Articles