webentwicklung-frage-antwort-db.com.de

Warum können Schnittstellen keine statischen Methoden angeben?

Ich weiß, dass diese Frage immer wieder gestellt wurde, aber es scheint, dass ich keine guten Antworten finde. Um klar zu machen, was ich zu wissen versuche, teile ich dies in zwei Fragen auf:

  1. Warum können Schnittstellen keine statischen Methodensignaturen haben? Ich werde versuchen, die Nicht-Antworten zu vereiteln und zu fragen, warum in der Welt ich dies mit folgendem tun möchte: Ich möchte GetDbConnectionType() statisch auf SqliteCodeGenerator und MssqlCodeGenerator aufrufen können:

    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. Auf der anderen Seite, und das ist die Angelegenheit dieser zweiten Frage: Wenn Sie eine gute Alternative kennen, um das oben genannte Ziel zu erreichen, dann auf jeden Fall ...

41
user610650

Angenommen, Sie könnten in einer Schnittstelle angeben, dass ein Typ eine bestimmte statische Methode haben muss ... wie würden Sie es nennen? Polymorphismus funktioniert durch Instanzen - während statische Mitglieder explizit keine Instanzen verwenden .

Nun gibt es eine Situation, in der ich sehen kann, wie statische Schnittstellenelemente funktionieren: generische Typen. Zum Beispiel:

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

Das würde das statische Element vom konkreten Typ T aufrufen.

Ich habe mehr darüber gebloggt , aber ich vermute, dass der Nutzen die Komplexität nicht rechtfertigt.

In Bezug auf Alternativen - normalerweise hätten Sie eine andere Schnittstelle und unterschiedliche Typen, um diese Schnittstelle zu implementieren. Das funktioniert in manchen Zusammenhängen gut, in anderen nicht.

65
Jon Skeet

@ JonSkeet: Es ist möglich, ein statisches Interface-Mitglied in CIL zu erstellen . Ich fürchte, Ihre erste Aussage ist irreführend. Ich nehme an, es wurde von C # als Entwurfsentscheidung des Microsoft-Teams ausgelassen, um die korrekte Verwendung von Schnittstellen zu fördern.

Die beste Möglichkeit, diese Funktionalität zu erhalten, ist wahrscheinlich mit Erweiterungsmethoden . Mit diesen Methoden können Sie allen Vererbern Ihres Interfaces oder einer bestimmten Implementierung dieses Interfaces eine Methode hinzufügen Implementierung der Erweiterungsmethode, die (wenn nicht geplant) leicht den Überblick verlieren kann.

7
Dead.Rabit

Es kann hilfreich sein, wenn eine Schnittstelle eine statische Klasse angeben könnte, sodass Mitglieder dieser Klasse vom Compiler als statische Mitglieder dieser Schnittstelle betrachtet werden. Anstatt die statische Klasse Enumerable<T> zu verwenden, um Enumerable<T>.Default zu erhalten, könnte man stattdessen IEnumerable<T>.Default syntaktisch angeben.

Noch hilfreicher wäre es, wenn eine Schnittstelle angeben könnte, dass einige dieser statischen Methoden auf ähnliche Weise wie Erweiterungsmethoden verwendet werden können, jedoch ohne die damit verbundenen merkwürdigen Bereichsregeln (eine Schnittstelle könnte also mehrere "Komfort" -Überlastungen bieten einige Memberfunktionen, ohne dass alle Implementierungen diese bereitstellen müssen).

Es wäre äußerst hilfreich, wenn Interface-Methoden in Kombination mit einem solchen Feature als "optional" deklariert werden könnten. Wenn also eine Implementierung eine Methode bereitstellt, wird sie verwendet. Wenn dies nicht der Fall ist, wird die Methode extension-ish automatisch ersetzt. Dies würde jedoch wahrscheinlich Änderungen an der CLR erfordern.

Da Schnittstellen keine statischen Klassen enthalten, können Sie am besten statische Klassen bereitstellen, die für Benutzer der Schnittstelle hilfreich sein werden, auch wenn der Compiler diese Klassen und Schnittstellen als völlig unabhängige Entitäten betrachtet.

1
supercat

Jons Antwort deckt so ziemlich alles ab, daher beinhaltet meine Antwort nur eine mögliche Umgehung der Verwendung der .NET-Konfigurations-API. Sie erfordert etwas Syntax-Overhead, gibt Ihnen jedoch statischen Zugriff auf die Instanz.

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

Eine Art Problemumgehung (obwohl es auf diese Weise tatsächlich besser sein kann), für die ich mich entschieden habe, ist die Verwendung einer statischen Instanz anstelle einer statischen Schnittstelle.

Eher, als:

// 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) {
   }
}

Definieren Sie eine Klasse (generieren Sie sie, wenn die Logik zwischen den verwendeten Klassen variieren muss):

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

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

und geben Sie Ihrer statischen Klasse eine statische Instanz dieser Klasse:

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

Das einzige Problem ist, dass Sie entscheiden müssen, ob Sie eine Eigenschaft für die statische Instanz verfügbar machen möchten:

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

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

...

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

oder um seine Methoden einzuwickeln:

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

Ich weiß, das ist alt, aber tatsächlich können Sie statische Funktionen in einer statischen Klasse außerhalb eines Namensbereichs deklarieren.

aber so wie Sie es machen, würden Sie die Funktion in der abstrakten Klasse statisch machen

um es von einer Schnittstelle aus zu tun, tun Sie dies

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