web-dev-qa-db-ja.com

インターフェースが静的メソッドを指定できないのはなぜですか?

私はこの質問が何度も尋ねられていることを知っていますが、十分な答えを見つけることができないようです。したがって、私が知りたいことを明確にするために、これを2つの質問に分けます。

  1. インターフェイスに静的メソッドシグネチャを設定できないのはなぜですか?私は世界でなぜこれを行うのかを尋ねる非回答を先取りしようとします:SqliteCodeGeneratorおよびMssqlCodeGeneratorで静的にGetDbConnectionType()を呼び出すことができます

    interface ICodeGenerator
    {
        // this is the method I would like to be static:
        string GetDbConnectionType();
    }
    
    abstract class CodeGeneratorBase : ICodeGenerator
    {
        public abstract string GetDbConnectionType();
    
        public void GenerateSomeCode(StringBuilder s)
        {
            s.AppendLine("var foo = new " + GetDbConnectionType() + "();");
        }
    }
    
    class SqliteCodeGenerator : CodeGeneratorBase
    {
        public override string GetDbConnectionType()
        {
            return "SQLiteConnection";
        }
    }
    
    class MssqlCodeGenerator : CodeGeneratorBase
    {
        public override string GetDbConnectionType()
        {
            return "SqlConnection";
        }
    }
    
  2. 一方、これはこの2番目の質問の問題です。前述の目標を達成するための優れた代替案を知っているなら、どうしても...

42
user610650

couldインターフェースで型に特定の静的メソッドが必要であると指定したとしましょう...どのように呼び出しますか?ポリモーフィズムはinstancesを介して機能します-一方、静的メンバーは明示的にdo n'tを使用しません。

さて、そうは言っても、one静的インターフェイスメンバーが動作しているのを見ることができる状況があります:ジェネリック型。例えば:

// This isn't valid code...
public void Foo<T>() where T : ICodeGenerator
{
    string type = T.GetDbConnectionType();
}

それは、concreteタイプTの静的メンバーを呼び出します。

私は これについてもっとブログを書いた ですが、その利点は複雑さを正当化するものではないと思います。

代替案の観点から-通常、別のインターフェースがあり、そのインターフェースを実装するために別のタイプがあります。これは一部の状況ではうまく機能しますが、他の状況では機能しません。

65
Jon Skeet

@JonSkeet: CILで静的インターフェイスメンバーを作成する が可能なため、最初のステートメントが誤解を招く恐れがあります。インターフェイスの正しい使用を促すために、Microsoftチームが設計上の選択としてC#から省略したと思います。

この機能を取得する最良の方法は、おそらく 拡張メソッド を使用することです。これにより、メソッドをインターフェイスのすべての継承者またはそのインターフェイスの特定の実装に追加できますが、別のクラスを記述する必要があります(計画されていない場合)簡単に追跡できなくなる可能性のある拡張メソッドの実装を保持します。

7
Dead.Rabit

インターフェースが静的クラスを指定できれば、コンパイラーはそのクラスのメンバーをそのインターフェースの静的メンバーと見なすことができます。したがって、静的クラスEnumerable<T>を使用してEnumerable<T>.Defaultを取得する代わりに、IEnumerable<T>.Defaultを構文的に指定できます。

そのような静的メソッドを拡張メソッドと同様の方法で使用できるように、ただし関連する奇妙なスコープルールなしでインターフェースを指定できるとさらに便利です(したがって、インターフェースは、複数の「便利な」オーバーロードをすべての実装がそれらを提供することを必要としないいくつかのメンバー関数)。

このような機能と組み合わせて、インターフェイスメソッドを「オプション」と宣言できれば、実装でメソッドが提供されたときに使用され、拡張されていないメソッドは自動的に置き換えられます。ただし、これにはおそらくCLRの変更が必要になります。

いずれの場合でも、インターフェイスには静的クラスが含まれないため、コンパイラがこれらのクラスとインターフェイスを完全に独立したエンティティと見なしても、インターフェイスのユーザーが役立つと思われる静的クラスを提供するのが最善です。

1
supercat

Jonの答えはほとんどすべてを網羅しているので、私の答えには.NET構成APIを使用した回避策のみが含まれています。少しの構文オーバーヘッドが必要ですが、インスタンスへの静的アクセスが可能です。

interface IStorage
{
    void Store(string item);
}

static class Storage
{
    private static readonly IStorage _instance;

    static Storage()
    {
        var storageTypeString = ConfigurationManager.AppSettings["storageTypeString"];
        var storageType = Type.GetType(storageTypeString, true);
        _instance = (IStorage)Activator.CreateInstance(storageType);
    }

    public static void Store(string item)
    {
        _instance.Store(item);
    }
}
1
ChaosPandion

このための回避策の一種(実際にはこの方法のほうが良いかもしれませんが)使用することにしたのは、静的インターフェイスの代わりに静的インスタンスを使用することです。

のではなく:

// does not compile
ISomeInterface {
   static void DoSomething();
   static bool TestSomething(string pValue);
   // etc... 
}

static class SomeStaticClass : ISomeInterface {
   public static void DoSomething() {
   }

   public static bool TestSomething(string pValue) {
   }
}

クラスを定義します(使用するクラス間でロジックを変更する必要がある場合は、汎用にします):

sealed class SomeClass {
   public void DoSomething() {
      // reusable implementation
   }

   public bool TestSomething(string pValue) {
      // reusable implementation
   }
}

そのクラスの静的インスタンスを静的クラスに渡します。

static class SomeStaticClass {
   static readonly SomeClass sSomeClass = new SomeClass();
}

唯一の問題は、プロパティを静的インスタンスに公開するかどうかを決定する必要があることです。

static class SomeStaticClass {
   static readonly SomeClass sSomeClass = new SomeClass();

   public static SomeClass SomeProperty { get { return sSomeClass; } }
}

...

SomeStaticClass.SomeProperty.DoSomething();
if (SomeStaticClass.SomeProperty.TestSomething(someValue))
   ...

またはそのメソッドをラップする:

static class SomeStaticClass {
   static readonly SomeClass sSomeClass = new SomeClass();

   public static void DoSomething() {
      sSomeClass.DoSomething();
   }

   public static bool TestSomething(string pValue) {
      sSomeClass.TestSomething(pValue);
   }
}

...

SomeStaticClass.DoSomething();
if (SomeStaticClass.TestSomething(someValue))
   ...
0
Dave Cousineau

私はこれが古いことを知っていますが、実際には名前空間外の静的クラスで宣言された静的関数を使用できます。

しかし、彼らはそれを置く方法で、あなたは単に抽象クラスで関数を静的にするでしょう

あなたがこれを行うインターフェースからそれを行うには

public static class Interfacefunction{
  public static string GetDbConnectionType(this ICodeGenerator me)
  {
      // this is the method I would like to be static:
      // you can even get access to me
      return "SQLiteConnection";
  }
}
0
Ron Bowes