web-dev-qa-db-ja.com

インターフェイスでメンバー関数を宣言する

まず、私はC#にかなり慣れていません。次のコードのように、インターフェイスでメンバー関数を宣言したいと思います

interface IMyInterface {
    void MyAction() {
        // do stuff depending on the output of function()
    }
    void Function();
}

ここで、Functionは純粋な仮想であり、IMyInterfaceの子によって実装される必要があります。インターフェイスの代わりに抽象クラスを使用できましたが、他のクラスから継承できませんでした...たとえば、MyActionがディレクトリでファイルを再帰的に検索し、Functionを任意のファイルに適用しているとします。私の例を明確にすることがわかりました。

インターフェイスがクラスを実装できないという制約を克服するために、デザインを変更するにはどうすればよいですか?

編集:C++では、テンプレートをそのまま使用します

template<class A>
static void MyAction(const A& a) {
    // do stuff depending on the output of A::Function()
};

class MyClass {
    void Function();
};

C#のインターフェイスを使用してこれを行うエレガントな方法があるかどうか疑問に思いました。

9
vanna

C#では、多重継承はありません。 compositionを使用すると、この制限を回避できます。

このようにインターフェースを定義します(Functionをここで定義する必要はありません):

public interface IMyInterface
{
    void MyAction();
}

抽象Functionを使用して抽象クラスを宣言し、このインターフェースを実装します。

public abstract class MyInterfaceBase : IMyInterface
{
    public void MyAction()
    {
        // Do stuff depending on the output of Function().
        Function();
    }

    protected abstract void Function();
}

この抽象クラスから、具体的な実装を導き出すことができます。これはまだ「最終」クラスではありませんが、作成に使用されます。

public class ConcreteMyInterface : MyInterfaceBase
{
    protected override void Function()
    {
        Console.WriteLine("hello");
    }
}

それでは、「最終的な」構成されたクラスに行きましょう。 SomeBaseClassから派生し、IMyInterfaceの機能を統合することでConcreteMyInterfaceを実装します。

public class SomeBaseClass
{
}

public class MyComposedClass : SomeBaseClass, IMyInterface
{
    private readonly IMyInterface _myInterface = new ConcreteMyInterface();

    public void MyAction()
    {
        _myInterface.MyAction();
    }
}

[〜#〜]更新[〜#〜]

C#では、ローカルクラスを宣言できます。構成クラス内のすべてを派生させることができるため、これは多重継承にさらに近づきます。

public class MyComposedClass : SomeBaseClass, IMyInterface
{
    private readonly IMyInterface _myInterface = new ConcreteMyInterface();

    public void MyAction()
    {
        _myInterface.MyAction();
    }

    private class ConcreteMyInterface : MyInterfaceBase
    {
        protected override void Function()
        {
            Console.WriteLine("hello");
        }
    }
}

これを直接処理する唯一の方法は、抽象クラスを使用することです。インターフェイスにはどのような形式の「ロジック」も含めることができず、単なるコントラクトであるためです。

ただし、1つの代替方法は、インターフェイスと静的クラスを作成することです。次に、インターフェイスを使用してロジックを拡張メソッドに配置できます。

public interface IMyInterface {
    void Function();
}

public static class MyInterfaceExtensions {
    public static void MyAction(this IMyInterface object)
    {
       // use object.Function() as needed
    }
}

ここでの主な欠点は、より多くのタイプであり、保守性が低下し、発見可能性が欠如します。

11
Reed Copsey

MyAction拡張メソッド :として定義できます。

public interface IMyInterface
{
   void Function();
}

public static class MyInterfaceExtensions
{
    public static void MyAction(this IMyInterface obj)
    {
        obj.Function();
    }
}

例:

public class HelloWorld : IMyInterface
{
    public void Function()
    {
        Console.WriteLine("Hello World");
    }

    public static void Main(string[] args)
    {
        new HelloWorld().MyAction();
    }
} 

出力:

こんにちは世界
7
dtb

その目的のために 。抽象クラスを定義する必要があります。デフォルトの実装を提供することも、実装を派生クラスに任せることもできます。

派生クラスが何かをオーバーライドしたい場合は、いつでもそれを行うことができます。これにより、オーバーライドしたい変更とともにベースを使用できる柔軟性が得られます。

2
Tabish Sarwar

インターフェイスは、単なるコントラクトである動作を実装できません。コントラクトの定義中にロジックを実装する場合は、抽象クラスを使用できます。

2
Claudio Redi

インターフェイスで関数のインターフェイス(署名と戻り値の型)を宣言してから、そのインターフェイスを実装するように定義された抽象クラスを作成し、抽象クラスに基本的なデフォルトの実装を実装します。次に、抽象クラスから継承する他の具象クラスを作成しますが、必要に応じて、抽象クラスの基本実装を別の実装でオーバーライドします。

1
Charles Bretana

この種の問題は、外部の動作を分離することで最もよく克服できる可能性があります。 MyActionこの場合、内部実装から。 MyFunction

ここでのポイントは、このクラスと他のクラスとの間のインターフェース/コントラクトの一部となるべきものと、そのコントラクトの実装の一部となるべきものを理解することです。

ここでは、このオブジェクトとそのコンシューマーの間のコントラクトが定義されています。

interface IMyInterface
{
    void MyAction();
}

現在、このインターフェイスを実装し、特定の動作を強制する基本クラス。

abstract class BaseClass : IMyInterface
{
    public void MyAction()
    {
        // do some commmon action

        // call derived implementation to deal with the outcome
    }

    protected abstract MyFunvoid ction();
}

そして最後に、MyFunctionの結果を特定の方法で処理する具体的な実装。

class ConcreteClass : BaseClass
{
    protected override void MyFunction()
    {
         // concrete iomplementation here
    }
}
1
RJ Lohan

インターフェイスはコントラクトであり、実装を含めることはできません。

上記のステートメントから:

インターフェイスの代わりに抽象クラスを使用できましたが、他のクラスから継承できませんでした

「C#が多重継承をサポートしないのはなぜですか」という質問に答えていると思います。

これは C#のシミュレートされた多重継承 に関するCodeProjectの記事です。このパターンに従って、C#の単純な継承モデルの回避策を実行できるはずです。

0
Philip Tenn

これは、C#8.0で提案されている機能です。

interface IA
{
    void M() { WriteLine("IA.M"); }
}

class C : IA { } // OK

IA i = new C();
i.M(); // prints "IA.M"`

https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md

0
Duanne