.Net接口

时间:2015-06-23 14:12:44

标签: c# .net interface backwards-compatibility missingmethodexception

假设我们在C#中定义了一个基本接口,如下所示:

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

然后我们有一个派生界面如下:

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

这些接口在API程序集中定义,自定义应用程序可以编译和运行。 (程序集还包括实现这些接口的非公开类和用于获取实例的公共工厂方法)。所有现存代码都使用ISub1,没有直接引用IBase的现有代码。它是以这种方式完成的,因为我们最终可能希望引入第二个派生接口ISub2,作为ISub1的同伴,现在已经过去了。不幸的是,虽然我们发现ISub2不应包含Prop2(仅限Prop1和其他一些独特属性),因此我们希望"降级"该属性下降到ISub1,导致以下修订的接口:

interface IBase
{
    int Prop1 { get; set }
}

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

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

鉴于没有IBase的消费者,似乎我们应该能够不受惩罚地做到这一点(而且我相当确定我们可以用Java做到这一点),但是当试图这样做时我们遇到了与旧接口定义编译的代码的二进制兼容性问题。具体做法是:

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

此代码在针对新接口定义运行时失败,异常如下:

  

System.MissingMethodException:找不到方法:' Void   MyNamespace.IBase.set_Prop2(System.String)'

请注意IBase的引用。我认为这是因为对ISub1.set_Prop2的看似调用已编译成与Prop2中实际引入IBase的地方紧密绑定。

任何人都可以帮我解决这个难题吗?即有没有办法重新考虑接口因素,以便ISub2的定义是" clean" (不包括无关的Prop2)?要求重新编译所有现有应用程序是不可能的。

4 个答案:

答案 0 :(得分:2)

有点hacky并且不确定它会起作用但是也许值得一试

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 :(得分:1)

通过在TryRoslyn中编写它,很明显根据您将属性放在界面中的位置存在差异:

假设:

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; }
}

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

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

(请注意,在这两种情况下我都使用C#代码中的ISub1*接口)

生成的IL代码是:

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)

所以IL代码"正确"解析为真正定义属性的接口。

答案 2 :(得分:1)

基本上,这个:

interface ISub1: IBase

只是说&#34;任何实现ISub1的类都承诺也会实现IBase&#34;。在每个界面中定义的方法没有混合,因此它也意味着&#34; ISub1包含3个属性,Prop1 - Prop3&#34;。< / p>

这就是它无法正常工作的原因。目前,ISub1被定义为只需要一个名为Prop3的属性。

答案 3 :(得分:0)

首先,您应该ISub2.Prop2隐藏ISub2。然后,根据Prop2不应包含this.addOrder = function(new_order) { return $http.post(base + "/add", angular.toJson(new_order)); }; 的原因,您应该使用implementing it explicitly属性弃用该实现,或者从两个访问器中抛出ObsoleteAttribute