web-dev-qa-db-ja.com

C#インターフェイスのメソッドのデフォルト実装?

デフォルトの実装を持つC#でインターフェイスを定義することは可能ですか? (その特定のデフォルトメソッドを実装せずに、そのインターフェイスを実装するクラスを定義できるようにするため)。

私は拡張メソッドを知っています(たとえば このリンク で説明されています)。しかし、それは私の答えですnot次のようなメソッド拡張を持っているので、コンパイラはMyClassでMyMethodを実装することについてまだ文句を言います:

public interface IMyInterface
{
    string MyMethod();
}

public static class IMyInterfaceExtens
{
    public static string MyMethod(this IMyInterface someObj)
    {
        return "Default method!";
    }
}

public class MyClass: IMyInterface
{
// I want to have a default implementation of "MyMethod" 
// so that I can skip implementing it here
}

(少なくとも私が理解している限り)Java( here を参照)でそうすることが可能であるため、これを求めています。

PS:abstract base classをいくつかのメソッドで持つこともnotC#に多重継承がないという理由だけで私の答えですまた、インターフェースのデフォルトの実装とは異なります(可能な場合!)。

30
Akhir

私はゲームを開発するので、多くの場合、インターフェイスのすべての実装に共通の機能が必要ですが、同時にサブクラスの仮想/オーバーライドメソッドが機能するように、各実装にも独自の機能を許可します。

これは私がそれを行う方法です:

public class Example
{
    void Start()
    {
        WallE wallE = new WallE();
        Robocop robocop = new Robocop();

        // Calling Move() (from IRobotHelper)
        // First it will execute the shared functionality, as specified in IRobotHelper
        // Then it will execute any implementation-specific functionality,
        // depending on which class called it. In this case, WallE's OnMove().
        wallE.Move(1);

        // Now if we call the same Move function on a different implementation of IRobot
        // It will again begin by executing the shared functionality, as specified in IRobotHlper's Move function
        // And then it will proceed to executing Robocop's OnMove(), for Robocop-specific functionality.
        robocop.Move(1);

        // The whole concept is similar to inheritence, but for interfaces.
        // This structure offers an - admittedly dirty - way of having some of the benefits of a multiple inheritence scheme in C#, using interfaces.
    }

}

public interface IRobot
{
    // Fields
    float speed { get; }
    float position { get; set; }

    // Implementation specific functions.
    // Similar to an override function.
    void OnMove(float direction);
}

public static class IRobotHelper
{
    // Common code for all IRobot implementations. 
    // Similar to the body of a virtual function, only it always gets called.
    public static void Move(this IRobot iRobot, float direction)
    {
        // All robots move based on their speed.
        iRobot.position += iRobot.speed * direction;

        // Call the ImplementationSpecific function
        iRobot.OnMove(direction);
    }
}

// Pro-Guns robot.
public class Robocop : IRobot
{
    public float position { get; set; }

    public float speed { get; set;}

    private void Shoot(float direction) { }

    // Robocop also shoots when he moves
    public void OnMove(float direction)
    {
        Shoot(direction);
    }
}

// Hippie robot.
public class WallE : IRobot
{
    public float position { get; set; }

    public float speed { get; set; }

    // Wall-E is happy just moving around
    public void OnMove(float direction) { }
}

C#v8では、インターフェイスでの具体的なメソッド実装も可能になります。これにより、将来実装されるインターフェースを変更しても、具体的な実装クラスが壊れないようになります。

そのため、次の言語バージョンでは次のようなことが可能になります。

interface IA
{
    void NotImplementedMethod();
    void M() { WriteLine("IA.M"); } //method definition present in the interface
}

このGitHub issue#288 を参照してください。また、Mads Torgersenは this チャンネル9のビデオでこの機能について詳しく説明しています。

:RTM状態のC#言語の現在のバージョンは、この回答を書いている時点ではv7です。

26
RBT

短い答え:

いいえ、インターフェイスにメソッドの実装を記述することはできません。

説明:

インターフェイスはコントラクトに似ているため、インターフェイスを継承する型は実装を定義する必要があります。デフォルトの実装でメソッドが必要な場合は、クラスを作成できます abstract 必要なメソッドのデフォルト実装を定義します。

例えば:

public abstract class MyType
{
    public string MyMethod()
    {
      // some implementation
    }

    public abstract string SomeMethodWhichDerivedTypeWillImplement();
}

そして今、Derviedクラスで:

public class DerivedType : MyType
{
  // now use the default implemented method here
}

UPDATE(C#8はこれをサポートします):

C#8では、インターフェイスにデフォルトの実装を許可します

15
Ehsan Sajjad

直接ではありませんが、インターフェイスの拡張メソッドを定義して、次のように実装できます

public interface ITestUser
{
    int id { get; set; }
    string firstName { get; set; }
    string lastName { get; set; }

    string FormattedName();
}

static class ITestUserHelpers
{
    public static string FormattedNameDefault(this ITestUser user)
    {
        return user.lastName + ", " + user.firstName;
    }
}

public class TestUser : ITestUser
{
    public int id { get; set; }
    public string firstName { get; set; }
    public string lastName { get; set; }

    public string FormattedName()
    {
        return this.FormattedNameDefault();
    }
}

編集*拡張メソッドと実装するメソッドの名前が異なることが重要です。そうしないと、スタックオーバーフローが発生する可能性があります。

6
Tim M

初心者のC#プログラマーとして、私はこのトピックを読んでいて、次のコード例が役に立つかどうか疑問に思っていました(これが適切な方法かどうかさえ知りません)。私にとっては、インターフェイスの背後にあるデフォルトの動作をコーディングできます。 (抽象)クラスを定義するためにジェネリック型の仕様を使用したことに注意してください。

namespace InterfaceExample
{
    public interface IDef
    {
        void FDef();
    }

    public interface IImp
    {
        void FImp();
    }

    public class AbstractImplementation<T> where T : IImp
    {
        // This class implements default behavior for interface IDef
        public void FAbs(IImp implementation)
        {
            implementation.FImp();
        }
    }

    public class MyImplementation : AbstractImplementation<MyImplementation>, IImp, IDef
    {
        public void FDef()
        {
            FAbs(this);
        }
        public void FImp()
        {
            // Called by AbstractImplementation
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyImplementation MyInstance = new MyImplementation();

           MyInstance.FDef();
        }
    }
}
0
Jurgen Wilsch