假设我们在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的定义是“干净的”(不包括无关的Prop2)?要求重新编译所有现有应用程序是不可能的。
有点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; }
}
通过在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代码“正确”解析为真正定义属性的接口。
基本上,这个:
interface ISub1: IBase
只是说“任何实施ISub1
的类都将承诺也实施IBase
”。每个界面中定义的方法没有混合,因此它也意味着“ISub1
包含3个属性,Prop1
- Prop3
”。
所以这就是为什么它不起作用。 ISub1
目前被定义为只需要一个名为Prop3
的属性。
首先,你应该通过ISub2.Prop2
隐藏implementing it explicitly。然后,根据为什么ISub2
不应该包含Prop2
,你应该使用ObsoleteAttribute属性弃用该实现,或者从两个访问器中抛出InvalidOperationException。
虽然这个问题很老,但我想提到一个类似的问题,当我将一个接口拆分成一个基类并继承类型时。因为它是具有相同主要版本的Nuget包的一部分,所以它必须向下兼容以前的版本。我通过使用“new”关键字复制原始界面中的成员来解决它。