2

インターフェイスの実装に基本的な構文の問題があるようです。基本的に私はこれを持っています:

    public interface IMarkerInterface
    {       
    }

    public class ConcreteObject : IMarkerInterface
    {
    }

    public interface IDoStuffInterface
    {
        void DoStuff(IMarkerInterface obj);

        // also doesn't work
        // void DoStuff<T>(T obj) where T : IMarkerInterface;
    }

    public class ConcreteDoStuff : IDoStuffInterface
    {
        public void DoStuff(ConcreteObject c)
        {

        }
    }

私の考えでは、ConcreteObject実装IMarkerInterfaceするので、実装するConcreteDoStuff.DoStuff()必要がありますIDoStuffInterface

しかし、コンパイルエラーが発生します"Error ConcreteDoStuff does not implement interface IDoStuffInterface.DoStuff()"

どうして?

4

6 に答える 6

5

実装されたメソッドには、インターフェイスとまったく同じ署名が必要です。すべての「ConcreteObject」オブジェクトは「IMarkerInterface」タイプですが、すべての「IMarkerInterfaces」が「ConcreteObject」になるわけではありません。したがって、2 つの署名は同等ではありません。インターフェイスは、その型のオブジェクトに有効なメソッドが実装されていることを CLR に保証できる必要があります。

于 2012-08-14T22:23:15.433 に答える
4

を実装するオブジェクトを指定すると、を実装する任意のオブジェクトを引数としてIDoStuffInterfaceメソッドを呼び出すことができると期待されますよね? ただし、必要なことが可能である場合は、何らかの形で(インターフェイスのユーザーとしては見えない) でのみ呼び出すことができますが、 を実装する他のオブジェクトは呼び出すことができません。したがって、あなたが望むことは実際には不可能です ( Liskov Substitution Principleに違反するため、望ましくありません)。DoStuffIMarkerInterfaceDoStuffConcreteObjectIMarkerInterface

ただし、必要なことは、インターフェイスのメソッドを実装する明示的なインターフェイス メンバーの実装を記述し、その横にDoStuff(IMarkerInterface)通常のメソッドの実装を提供することで実行できます。DoStuff(ConcreteObject)インターフェイスのすべてのユーザーはメソッドを表示して呼び出すことしかできDoStuff(IMarkerInterface)ませんが、のユーザーはメソッドConcreteDoStuffを直接呼び出すことしかできませんDoStuff(ConcreteObject)。ユーザーがとにかくインターフェイス メソッドを呼び出したい場合は、オブジェクトを最初にキャストする必要があります。IDoStuffInterface

public class ConcreteDoStuff : IDoStuffInterface
{
    // Explicit interface member implementation:
    // This method is not directly visible as a member of the class.
    void IDoStuffInterface.DoStuff(IMarkerInterface obj)
    {
        // Do something with 'obj', or throw an exception when
        // it has the wrong type. Delegate the call to the
        // other DoStuff method if you wish.
    }

    // Normal method, technically not related to the interface method:
    public void DoStuff(ConcreteObject c)
    {
        // Do your thing.
    }
}

編集:

興味深い回避策!これは常識だと思いますか?それとも回避策は回避すべき「ハック」ですか?

インターフェイスを明示的に実装する手法は、よく知られており、よく理解されており、非常に一般的です。ハッキングとは見なされず、回避する必要はありません。まれに、同じ名前のメンバーを定義する 2 つのインターフェイスをクラスが実装しているが、それらに異なる動作を与えたい場合でも必要です。

ただし、許可された入力値またはアクションを制限するすべての場合で、Liskov Substitution Principle (LSP) に違反します。私の例では、IMarkerInterface objパラメーターをConcreteObjectオブジェクトに制限します。その他の例としては、次のようなものがあります。オブジェクトが追加されCollection<T>たときに例外をスローする。引数の型が間違っている場合にエラーをスローnullする実装。IComparable.CompareToまたは、ReadOnlyCollectionそのAddメソッドを呼び出すときに例外をスローする です。

LSP に違反するべきではありませんが (コードがより (再) 使用可能になり、テストしやすくなります) 、.NET Framework 自体であっても、かなり頻繁に違反されています場合によっては、これに違反しないことが面倒であったり、コードが読みにくくなったり、不可能になることさえあります (たとえば、.NET Framework のクラスとインターフェイスの制限のため)。

私が言ったように、同じ「回避策」が の実装に適用されIComparableます。たとえば、同じタイプの他のオブジェクトとのみ比較できるクラスを実装するには、次のようにします。

public class BeanBag : IComparable, IComparable<BeanBag>
{
    private int beanCount;

    // Explicit interface member implementation:
    int IComparable.CompareTo(object other)
    {
        if (!(other is BeanBag))
            throw new ArgumentException("Wrong type!");
        // Calls the normal CompareTo() method.
        return CompareTo((BeanBag)other);
    }

    // Normal CompareTo method:
    public int CompareTo(BeanBag other)
    {
        if (other == null) return 1;
        return this.beanCount.CompareTo(other.beanCount);
    }
}
于 2012-08-14T22:27:48.017 に答える
2

実装クラスはConcreteDoStuff、コントラクトを変更しようとしています。一方的に。

于 2012-08-14T22:22:07.463 に答える
2

署名が一致するメソッドを実装する必要があります。

public class ConcreteDoStuff : IDoStuffInterface
{
    public void DoStuff(IMarkerInterface c) // not ConcreteObject c
    {

    }
}

ConcreteObject のインスタンスで ConcreteDoStuff.DoStuff を呼び出すことができることに注意してください。

 var concrete = new ConcreteObject()
 new ConcreteDoStuff().DoStuff(concrete);

うまく機能する場合は、これを行うこともできます (インターフェイスの明示的な実装)。

public class ConcreteDoStuff : IDoStuffInterface
{
    public void DoStuff(ConcreteObject c)
    {

    }

    void IDoStuffInterface.DoStuff(IMarkerInterface c)
    {
       // some implementation preferably related to
       // DoStuff(ConcreteObject) i.e.: 
       DoStuff(c as ConcreteObject); 
    }
}
于 2012-08-14T22:23:10.790 に答える
2

ConcreteObjectの実装でのみ受け入れることによりConcreteDoStuff.DoStuff()、渡すことができる引数のタイプが制限されます。

も実装する別のクラスを作成する場合IMarkerInterface:

public class ConcreteObject2 : IMarkerInterface
{
}

DoStuff()任意の実装に渡すことができるはずですが、定義によるものでConcreteObject2はないConcreteObjectため、他の回答が示しているように、契約に違反します。

于 2012-08-14T22:24:52.953 に答える
1

汎用インターフェースを使用するのが最善です:

public interface IMarkerInterface
{       
}

public class ConcreteObject : IMarkerInterface
{
}

public interface IDoStuffInterface<T>
{
    void DoStuff(T obj);
}

public class ConcreteDoStuff : IDoStuffInterface<ConcreteObject>
{
    public void DoStuff(ConcreteObject c)
    {

    }
}
于 2012-08-14T22:24:05.703 に答える