4

次のように C# で定義された基本インターフェイスがあるとします。

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

次に、次のような派生インターフェイスがあります。

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

これらのインターフェイスは、カスタム アプリケーションがコンパイルおよび実行される API アセンブリで定義されます。(アセンブリには、これらのインターフェイスを実装する公開されていないクラスと、インスタンスを取得するためのパブリック ファクトリ メソッドも含まれます)。現存するすべてのコードは を使用しておりISub1、直接参照する既存のコードはありませんIBaseISub2最終的に のピアとして2 番目の派生インターフェイス を導入する可能性があることを予期して、このように行われましたがISub1、それが実現しました。残念ながら、 には(Prop1 といくつかの追加の一意のプロパティのみ) をISub2含めるべきではないことがわかりました。そのため、そのプロパティを に「降格」して、次の改訂されたインターフェイスを作成します。Prop2ISub1

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た場所へのタイトなバインドでコンパイルされているためであると推測されます。Prop2IBase

この難問から抜け出す方法を教えてくれる人はいますか? つまり、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ます。各インターフェイス内で定義されたメソッドの混合はありません。つまり、「3 つのプロパティを含む」という意味でもありISub1ます。Prop1Prop3

それが機能していない理由です。ISub1は現在、 と呼ばれるプロパティを 1 つだけ要求するように定義されていますProp3

于 2015-06-23T14:51:31.787 に答える
0

まず、明示的ISub2.Prop2に実装して非表示にする必要があります。次に、 を含めない理由に応じて、 ObsoleteAttribute属性を使用してその実装を非推奨にするか、両方のアクセサーからInvalidOperationExceptionをスローする必要があります。ISub2Prop2

于 2015-06-23T14:57:10.937 に答える