42

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

  1. インターフェイスが静的メソッド シグネチャを持てないのはなぜですか? なぜ世界でこれをやりたいのかを尋ねる答えのないものを先取りしようとしGetDbConnectionType()ます:SqliteCodeGeneratorMssqlCodeGenerator

    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 番目の質問の問題ですが、前述の目標を達成するための適切な代替手段を知っている場合は、ぜひ...

4

6 に答える 6

65

型が特定の静的メソッドを持つ必要があることをインターフェイスで指定できるとします...どのように呼び出すでしょうか? ポリモーフィズムはインスタンスを通じて機能しますが、静的メンバーは明示的にインスタンスを使用しません

そうは言っても、静的インターフェイス メンバーが機能している状況が1 つあります。ジェネリック型です。例えば:

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

これは、具象型の静的メンバーを呼び出しますT

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

代替案に関しては、通常、別のインターフェースがあり、そのインターフェースを実装するための別のタイプがあります。これは、一部のコンテキストではうまく機能しますが、他のコンテキストではうまく機能しません。

于 2012-05-31T16:59:18.913 に答える
7

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

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

于 2014-07-17T10:37:46.287 に答える
1

インターフェイスが静的クラスを指定して、そのクラスのメンバーがコンパイラによってそのインターフェイスの静的メンバーとして認識されるようにすると、ある程度役立つ場合があります。したがって、静的クラスを使用Enumerable<T>してを取得Enumerable<T>.Defaultする代わりに、構文的にを指定することができますIEnumerable<T>.Default

インターフェイスが、そのような静的メソッドのいくつかを拡張メソッドと同様の方法で使用できるように指定できれば、さらに役立ちますが、それらに関連付けられた奇妙なスコープルールはありません(したがって、インターフェイスは、複数の「便利な」オーバーロードを提供するように見える可能性があります一部のメンバーは、それらを提供するためにすべての実装を必要とせずに機能します)。

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

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

于 2012-05-31T17:23:29.673 に答える
1

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);
    }
}
于 2012-05-31T17:11:23.433 に答える