4

假设我们在 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)?要求所有现有的应用程序重新编译是不可能的。

4

5 回答 5

2

有点老套,不确定它会起作用,但也许值得一试

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; }
}
于 2015-06-23T14:51:29.643 回答
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";

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

生成的 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 代码“正确”地解析为真正定义属性的接口。

于 2015-06-23T14:36:48.530 回答
1

基本上,这是:

interface ISub1: IBase

只是说“任何实现的类都ISub1将承诺也实现IBase”。每个接口中定义的方法没有混合,这也意味着“ISub1包含 3 个属性,Prop1- Prop3”。

所以这就是它不起作用的原因。ISub1当前被定义为只需要一个名为Prop3.

于 2015-06-23T14:51:31.787 回答
0

ISub2.Prop2首先,您应该通过显式实现来隐藏它。然后,根据ISub2不应该包含的原因Prop2,您应该使用ObsoleteAttribute属性弃用该实现,或者从两个访问器中抛出InvalidOperationException 。

于 2015-06-23T14:57:10.937 回答
0

虽然这个问题已经很老了,但我想提一下我在将接口拆分为基类和继承类型时遇到的类似问题。因为它是具有相同主要发布版本的 Nuget 包的一部分,所以它必须向下兼容以前的版本。我通过使用“new”关键字复制原始界面中的成员来解决它。

提取基本接口后的 MissingMethodException

于 2019-03-11T11:48:50.597 回答