7

次のクラスとインターフェースを検討してください。

    public interface A { string Property { get; set; } }

    public interface B { string Property { get; set; } }

    public interface C : A, B { }

    public class MyClass : C
    {
        public string Property { get; set; }
    }

シンプルに見えますよね?ここで、次のプログラムを検討してください。

    static void Main(string[] args)
    {
        MyClass myClass = new MyClass();
        myClass.Property = "Test";

        A aTest = myClass;
        B bTest = myClass;
        C cTest = myClass;

        aTest.Property = "aTest";
        System.Console.WriteLine(aTest.Property);
        bTest.Property = "bTest";
        System.Console.WriteLine(bTest.Property);
        cTest.Property = "cTest";
        System.Console.WriteLine(cTest.Property);
        System.Console.ReadKey();
    }

問題ないように見えますが、コンパイルされません。それは私に曖昧な例外を与えます:

スクリーンショット コンパイラ

C# がこれを理解できないのはなぜですか? 私がやっていることは、アーキテクチャの観点からクレイジーですか? 理由を理解しようとしています(キャストで解決できることはわかっています)。

編集

interface を導入したときに問題が発生しましたC。使っMyClass : A, Bてみると全く問題ありません。

最後の

このテーマに関するブログをちょうど完成させました: Interface Ambiguity and Implicit Implementation

4

8 に答える 8

8

要するに、それは確かに曖昧だからです。

今、より詳細な話。すでに見てきたように、明示的なインターフェイスの実装があるため、A.Property と B.Property に対して 2 つの異なる実装を持つことができます。C しかない場合、実装が同じかどうかを判断する方法はありません。C# の "哲学" は、あなたの意図を推測することではなく、必要に応じてより明確に述べるものであるため、コンパイラは A.Property または B.Property を選択せず​​、エラーを報告します。

于 2011-04-05T12:08:57.637 に答える
4

明示的なインターフェイスの実装が必要です:

public interface A { string Property { get; set; } }

public interface B { string Property { get; set; } }

public interface C : A, B { }

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
}

彼らに電話する時が来たら、あなたはしなければならないことがあります:

MyClass c = new MyClass();
Console.WriteLine("Property A is ": ((A)c).Property);

なぜあなたはしないのですか:

public class MyClass : C
{
    string B.Property { get; set; }
    string A.Property { get; set; }
    string B { get { return B.Property; } set { B.Property=value; } }
    string A { get { return A.Property; } set { A.Property=value; } }

}

また、これは悪い設計であることに注意してください。インターフェイス C を公開する場合は、A/B.Property を公開するためのより良い方法を見つけてください。

于 2011-04-05T11:58:47.593 に答える
3

何を理解するのですか?cTest は「C」型で、2 つの異なるクラスから「Property」を継承します。コンパイラは、どちらが必要かわかりません。この種の動作は C++ から継承されています。これは、「多重継承がパンドラの箱である理由」の典型的な例です。

他のオブジェクト指向言語 -- Java は注目に値する例です -- 定義により、この問題を回避します: 類似の名前/類似の署名のメソッドは、共通の子孫に融合されます。

于 2011-04-05T11:59:45.420 に答える
3

単一のインターフェイスから継承すると、コンパイラは、新しいメソッドを追加するときに、実装する必要があるメソッドを正確に判断できます。

ただし、複数のインターフェイスが同じメソッドを持っている場合、基本的な (そして正しい) 仮定は、これらのメソッドまたはプロパティが異なるインターフェイスで定義されているため、各インターフェイスがメソッドの異なる実装を期待しているということです。

そのため、コンパイラは、これらのさまざまなインターフェイスには、これらの各プロパティの明示的な実装が必要であることを通知します。

2 つのインターフェイスがプロパティまたはメソッドに対して同じ NAME を共有しているという事実は恣意的なものです。それらが名前以外のものを共有していると想定する理由はありません。そのため、コンパイラはそれらを暗黙のうちに同じように扱うという間違いを犯さないように保護します。

于 2011-04-05T12:09:07.480 に答える
2

シンプルじゃないし、見た目もシンプルじゃない。2 つのインターフェイス間で名前の競合が発生した場合、.NET は実装しようとしているインターフェイスを確認する必要があります。これを尋ねる方法は、あいまいなエラーを介することです。

この種のエラーがなければ、たまたまインターフェイスを実装することになります。

于 2011-04-05T11:57:59.990 に答える
2

各インターフェイスから両方のプロパティを明示的に実装する必要があります。

public class MyClass : C     
{         
    string A.Property { get; set; }    
    string B.Property { get; set; }      
} 
于 2011-04-05T11:59:08.270 に答える
0

明示的なインターフェイスの実装が問題に対する答えであるため、多くの答えがあり、それらはすべて正しいです。

少し複雑な例を使用して、この設計の背後にある動機を明確にしようとします。

実行する人々のためのインターフェースがあるとしましょう( 、 、 などの実装がLongDistanceRunner可能JoggerですMarathonMan

public interface IRunner 
{
   void Run();
}

電源を入れて実行できるデバイスのインターフェース (可能な実装BathTubApplicationDishwasherなど)

public interface IRunnable
{
   void Run();
}

今、私は( 、などの実装)のIMusicallJogger作成とインターフェースをしたいJoggerWithIpodBoomBoxJogger

public interface IMusicalJogger : IRunner, IRunnable {}

public class BoomBoxJogger : IMusicalJogger
{
   // code here
}

BoomBoxJogger bbJogger = new BoomBoxJogger();

さて、bbJogger.Run()私のオブジェクトは何をすべきでしょうか?公園を横切って走り始めるべきか、ラジカセをオンにするべきか、あるいはその両方か、またはまったく別の何かをすべきか? クラスとコールサイトの両方を実装すると、ジョガーに両方を実行させたいのは明らかかもしれませんが、コールサイトだけを制御するとどうなるでしょうか? そして、何か他のことをするインターフェースの他の実装がある場合はどうなるでしょうか? そして、私のジョガーが公園を横切って走り始めた場合、それがデバイスのように見なされるコンテキストで使用された場合 (キャスティングによって) はどうなるでしょうか。

そこで、明示的なインターフェースの実装が役立ちます。

クラスを次のように定義する必要があります。

public class BoomBoxJogger : IMusicalJogger
{
   void IRunner.Run() //implementation of the runner aspect
   {
      Console.WriteLine("Running through the park");
   }

   void IRunnable.Run() //implementation of the runnable aspect
   {
      Console.WriteLine("Blasting out Megadeth on my boombox");
   }

   public void Run() //a new method defined in the class itself 
   {
      Console.WriteLine("Running while listening to music");
   }

}

そして、呼び出すときに、使用したいジョガーの側面を指定する必要があります。

BoomBoxJogger bbJogger = new BoomBoxJogger();
((IRunner).bbJogger).Run(); // start running
((IRunnable).bbJogger).Run(); // blast the boombox
//and of course you can now do
bbJogger.Run //running while listening

((IMusicalJogger)jogger).Run(); //compiler error here, as there is no way to resolve this.

コンセプトを明確にするのに役立つことを願っています。

于 2011-04-05T12:33:55.173 に答える
0

あなたのしていることは正しくないからです。A と B が衝突しており、プロパティの名前が同じです...インターフェイスの明示的な実装を使用する必要があります。

ここを参照してください。

于 2011-04-05T11:59:56.007 に答える