Pourquoi les paramètres facultatifs C # 4 définis sur l'interface ne sont-ils pas appliqués sur la classe d'implémentation?

361

J'ai remarqué qu'avec les paramètres facultatifs en C # 4 si vous spécifiez un paramètre facultatif sur une interface que vous n'avez pas, ne devez pas rendre ce paramètre facultatif sur n'importe quelle classe d'implémentation:

public interface MyInterface
{
    void TestMethod(bool flag = false);
}

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}

et donc:

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

Est-ce que quelqu'un sait pourquoi les paramètres facultatifs sont conçus pour fonctionner de cette façon?

D'une part, je suppose que la possibilité de remplacer toutes les valeurs par défaut spécifiées sur les interfaces est utile, mais pour être honnête, je ne sais pas si vous devriez même être en mesure de spécifier des valeurs par défaut sur l'interface car cela devrait être une décision d'implémentation.

D'un autre côté, cette déconnexion signifie que vous ne pouvez pas toujours utiliser la classe concrète et l'interface de manière interchangeable. Bien sûr, cela ne serait pas un problème si la valeur par défaut est spécifiée sur l'implémentation, mais si vous exposez votre classe concrète comme interface (en utilisant un cadre IOC pour injecter la classe concrète par exemple), alors vraiment il n'y a pas point ayant la valeur par défaut car l'appelant devra toujours la fournir de toute façon.

theburningmonk
la source
22
Parce qu'ils sont facultatifs?
Odé
1
Mais vous pouvez lancer l'instance d'objet MyInterfaceet l' appeler avec le paramètre optionnel: ((MyInterface)obj).TestMethod();.
Jim Mischel
7
@oded - mais si vous dites que ce paramètre est facultatif dans le contrat, pourquoi autorisez-vous l'implémenteur à ne pas le rendre facultatif? cela ne cause-t-il pas simplement de la confusion à quiconque souhaite utiliser le contrat?
theburningmonk
1
Je pense que dans ce cas, vous pouvez dire que le paramètre est facultatif dans l'implémentation, pas dans l'appel des méthodes d'implémentation.Lorsque vous appelez la méthode dans la classe, vous devez suivre les règles de la classe (le paramètre n'est pas facultatif dans la classe, vous pouvez donc n'appelez pas la méthode sans elle), et dans la seconde main lorsque vous implémentez l'interface, vous devez suivre les règles d'interface, de sorte que vous pouvez remplacer les méthodes avec / sans paramètres facultatifs. Juste une opinion.
Mohamad Alhamoud
2
Plus d'explication détaillée du problème ici -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Andrew Orsich

Réponses:

236

MISE À JOUR: Cette question a fait l'objet de mon blog le 12 mai 2011. Merci pour la grande question!

Supposons que vous ayez une interface comme vous le décrivez, et une centaine de classes qui l'implémentent. Ensuite, vous décidez de rendre facultatif l'un des paramètres d'une des méthodes de l'interface. Suggérez-vous que la bonne chose à faire est que le compilateur force le développeur à trouver chaque implémentation de cette méthode d'interface et à rendre le paramètre facultatif également?

Supposons que nous ayons fait ça. Supposons maintenant que le développeur ne disposait pas du code source pour l'implémentation:


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}

// in source code
interface MyInterface 
{ 
    void TestMethod(bool b = false); 
}
class D : B, MyInterface {}
// Legal because D's base class has a public method 
// that implements the interface method

Comment l'auteur de D est-il censé faire fonctionner cela? Sont-ils obligés dans votre monde d'appeler l'auteur de B par téléphone et de leur demander de leur envoyer une nouvelle version de B qui rend la méthode un paramètre facultatif?

Ça ne va pas voler. Et si deux personnes appellent l'auteur de B et que l'une d'elles veut que la valeur par défaut soit vraie et que l'une d'elles veuille que ce soit faux? Et si l'auteur de B refuse simplement de jouer le jeu?

Dans ce cas, ils seraient peut-être tenus de dire:

class D : B, MyInterface 
{
    public new void TestMethod(bool b = false)
    {
        base.TestMethod(b);
    }
}

La fonctionnalité proposée semble ajouter beaucoup d'inconvénients pour le programmeur sans augmentation correspondante de la puissance représentative. Quel est l'avantage convaincant de cette fonctionnalité qui justifie l'augmentation des coûts pour l'utilisateur?


MISE À JOUR: Dans les commentaires ci-dessous, supercat suggère une fonctionnalité de langue qui ajouterait véritablement de la puissance à la langue et permettrait certains scénarios similaires à celui décrit dans cette question. Pour info, cette fonctionnalité - implémentations par défaut des méthodes dans les interfaces - sera ajoutée à C # 8.

Eric Lippert
la source
9
@supercat: Nous n'avons pas l'intention de le faire, mais une fonctionnalité comme celle-ci est possible. Cela a déjà été proposé. Au cours du processus de conception de C # 4, nous avons lancé beaucoup de fonctionnalités que nous appelions "extension tout" - nous avons des méthodes d'extension, donc que signifierait avoir des événements d'extension, des propriétés d'extension, des constructeurs d'extension, des interfaces d'extension, etc. . Les classes que vous pourriez «attacher» aux interfaces et dire «ces méthodes sont les implémentations par défaut de cette interface» sont un moyen possible de caractériser les «interfaces d'extension». Une idée intéressante.
Eric Lippert
16
clarifier mon raisonnement pour expliquer pourquoi je pense que ce n'est pas intuitif: pour moi, un paramètre facultatif est "facultatif pour l'appelant de la méthode" NON "facultatif pour le réalisateur de l'interface".
Stefan de Kok
8
"Sont-ils tenus dans votre monde d'appeler l'auteur de B par téléphone et de leur demander de bien vouloir leur envoyer une nouvelle version [...]?" Non, aucun appel téléphonique requis. B ne devrait tout simplement plus être considéré comme une implication de MyInterface et l'auteur de D pourrait en être informé par le compilateur. L'auteur de D serait tenu d'implémenter D avec une TestMethod qui accepte un paramètre par défaut, car c'est ce que l'auteur de l'interface requiert - vous vous opposez à ce que l'auteur de l'interface applique une contrainte car quelqu'un pourrait vouloir la casser. Ce n'est pas un bon argument.
philofinfinitejest
7
@EricLippert Dans le cas où le paramètre facultatif est spécifié pour la commodité du codage, oui, imposer des inconvénients dans la mise en œuvre est contre-intuitif. Mais dans le cas où le paramètre facultatif est utilisé pour minimiser la surcharge de méthode, une caractéristique puissante, ces valeurs facultatives peuvent revêtir une grande importance et les ignorer dilue certainement cette puissance. Sans parler du cas où l'implémentation concrète spécifie une valeur par défaut différente de celle de l'interface. Cela semble être une déconnexion très étrange à autoriser.
philofinfinitejest
8
Je suis d'accord avec @philofinfinitejest ici. Une interface indique ce que quelque chose peut faire. Si je reçois une implémentation d'une interface avec une valeur par défaut différente de celle spécifiée par l'interface, comment savoir cela? Hé, j'ai une interface qui passe vrai par défaut, alors pourquoi cette chose devient-elle fausse? Cela semblerait que je n'ai PAS obtenu l'interface que j'attendais. Pire encore, je dois maintenant programmer une implémentation et non une interface.
aaronburro
47

Un paramètre facultatif est simplement étiqueté avec un attribut. Cet attribut indique au compilateur d'insérer la valeur par défaut de ce paramètre sur le site d'appel.

L'appel obj2.TestMethod();est remplacé par obj2.TestMethod(false);lorsque le code C # est compilé en IL, et non au moment JIT.

Donc, d'une certaine manière, c'est toujours l'appelant qui fournit la valeur par défaut avec des paramètres facultatifs. Cela a également des conséquences sur le contrôle de version binaire: si vous modifiez la valeur par défaut mais ne recompilez pas le code appelant, il continuera à utiliser l'ancienne valeur par défaut.

D'un autre côté, cette déconnexion signifie que vous ne pouvez pas toujours utiliser la classe concrète et l'interface de manière interchangeable.

Vous ne pouvez déjà pas le faire si la méthode d'interface a été implémentée explicitement .

CodesInChaos
la source
1
Pouvez-vous cependant forcer les implémenteurs à implémenter explicitement?
écraser
30

Parce que les paramètres par défaut sont résolus au moment de la compilation, pas à l'exécution. Les valeurs par défaut n'appartiennent donc pas à l'objet appelé, mais au type de référence via lequel il est appelé.

Olhovsky
la source
7

Les paramètres facultatifs sont un peu comme une substitution de macro de ce que je comprends. Ils ne sont pas vraiment facultatifs du point de vue de la méthode. Un artefact de cela est le comportement que vous voyez lorsque vous obtenez des résultats différents si vous effectuez un cast sur une interface.

Ariel Arjona
la source