web-dev-qa-db-fra.com

Pourquoi les interfaces ne peuvent-elles pas spécifier des méthodes statiques?

Je sais que cette question a été posée à maintes reprises, mais je n'arrive pas à trouver de réponses suffisantes. Donc, pour clarifier ce que j'essaie de savoir, je vais scinder cela en deux questions:

  1. Pourquoi les interfaces ne peuvent-elles pas avoir des signatures de méthode statiques? Je vais essayer de préempter les non-réponses en demandant pourquoi, dans le monde, j'aimerais procéder de la manière suivante: J'aimerais pouvoir invoquer statiquement GetDbConnectionType() sur SqliteCodeGenerator et MssqlCodeGenerator:

    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. D’autre part, et c’est l’affaire de cette deuxième question, si vous connaissez une bonne alternative pour atteindre l’objectif précité, alors, par tous les moyens ...

41
user610650

Supposons que vous pourriez spécifier dans une interface qu'un type devait avoir une méthode statique particulière ... comment l'appelleriez-vous? Le polymorphisme fonctionne à travers instances - alors que les membres statiques explicitement ne utilisent pas les instances.

Cela dit, il y a one une situation dans laquelle je peux voir les membres de l'interface statique fonctionner: types génériques. Par exemple:

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

Cela appellerait le membre statique du type concreteT.

J'ai blogué plus à ce sujet , mais je soupçonne que l'avantage ne justifie pas la complexité.

En termes d'alternatives - vous auriez généralement une autre interface et des types distincts pour implémenter cette interface. Cela fonctionne bien dans certains contextes, mais pas dans d'autres.

65
Jon Skeet

@JonSkeet: Il est possible de créer un membre d'interface statique dans CIL , je crains donc que votre première déclaration soit trompeuse. Je suppose que l'équipe de Microsoft a omis de choisir C # dans la conception pour encourager l'utilisation correcte des interfaces.

Le meilleur moyen d’obtenir cette fonctionnalité est probablement avec méthodes d’extension , elles vous permettront d’ajouter une méthode à tous les héritiers de votre interface ou à une implémentation spécifique de cette interface. Toutefois, vous devez écrire une classe distincte pour contenir le fichier. mise en œuvre de la méthode d’extension qui (si non prévue) peut être facile à perdre de vue.

7
Dead.Rabit

Il pourrait être quelque peu utile qu'une interface puisse spécifier une classe statique, de sorte que les membres de cette classe soient vus par le compilateur comme des membres statiques de cette interface. Ainsi, au lieu de devoir utiliser la classe statique Enumerable<T> pour obtenir Enumerable<T>.Default, on pourrait spécifier syntaxiquement IEnumerable<T>.Default.

Il serait encore plus utile qu'une interface puisse spécifier que certaines de ces méthodes statiques doivent être utilisables de la même manière que les méthodes d'extension, mais sans les règles d'étendue étranges qui leur sont associées (une interface pourrait donc sembler offrir plusieurs surcharges de "commodité" pour certaines fonctions membres sans que toutes les implémentations soient nécessaires pour les fournir).

Il serait extrêmement utile que, combinées à une telle fonctionnalité, les méthodes d'interface puissent être déclarées "optionnelles", de sorte que lorsqu'une implémentation fournit une méthode, elle serait utilisée et que, dans le cas contraire, la méthode extension-ish serait automatiquement remplacée. Cela nécessiterait probablement des modifications du CLR, cependant.

Quoi qu'il en soit, comme les interfaces n'incluent pas de classes statiques, la meilleure solution est de fournir des classes statiques que les utilisateurs de l'interface trouveront utiles, même si le compilateur considérera ces classes et les interfaces comme des entités entièrement indépendantes.

1
supercat

La réponse de Jon couvrant à peu près tout, ma réponse ne comprend donc qu'une solution possible à l'aide de l'API de configuration .NET. Cela nécessite un peu de surcharge de syntaxe, mais cela vous donne un accès statique à l'instance.

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

Une sorte de solution de contournement (bien que cela puisse en réalité être mieux ainsi) que j'ai décidé d'utiliser consiste à utiliser une instance statique au lieu d'une interface statique.

Plutôt que:

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

Définissez une classe (rendez-la générique si la logique doit varier entre les classes avec lesquelles vous l'utilisez):

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

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

et donnez une instance statique de cette classe à votre classe statique:

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

Le seul problème est que vous devez décider d'exposer une propriété à l'instance statique:

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

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

...

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

ou pour envelopper ses méthodes:

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

Je sais que c'est vieux, mais en réalité, vous pouvez utiliser des fonctions statiques déclarées dans une classe statique en dehors d'un espace de noms.

mais ils la façon dont vous le mettez vous voudriez simplement rendre la fonction statique dans la classe abstraite

pour le faire depuis une interface, vous le faites

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