54 votes

Pourquoi est-il illégal d'avoir un setter privé sur une implémentation explicite d'une interface getter-only ?

J'ai tendance à privilégier les implémentations explicites des interfaces par rapport aux implémentations implicites, car je pense qu'il est généralement préférable de programmer en fonction de l'interface plutôt que d'une implémentation, et c'est souvent une nécessité lorsqu'on utilise des services Web.

Cela dit, je me demandais pourquoi ce qui suit est illégal avec une déclaration d'interface explicite et légal avec une déclaration implicite :

interface IConnection
{
    string ConnectionString { get; }
}

class Connection1 : IConnection
{
    // private set is illegal, won't compile
    string IConnection.ConnectionString { get; private set; }
}

class Connection2 : IConnection
{
    // private set is legal now, it is not part of the interface
    string ConnectionString { get; private set; }
}

Je sais comment résoudre ce problème, car il est légal d'avoir à la fois une interface explicite et implicite, et je peux rendre l'implémentation de l'interface implicite complètement privée.

Pourtant, je m'interroge sur le raisonnement qui sous-tend cette décision. Car techniquement, la méthode privée compilée en interne set_IConnection_ConnectionString n'a pas besoin de faire partie de l'interface, n'est-ce pas ? Il pourrait simplement être considéré comme un setter auxiliaire, ne faisant pas partie de l'interface, comme c'est le cas dans la situation d'implémentation implicite.

Mise à jour : en bonus, l'erreur de compilation apparemment confuse, et à mon avis pas tout à fait correcte, que vous recevez est la suivante :

Le modificateur d'accessibilité de l'accesseur doit être plus restrictif que la propriété Connection1.ConnectionString

Excusez-moi, plus restrictif que private comment... quoi ?

64voto

Damien_The_Unbeliever Points 102139

La seule façon de invoquez un membre d'interface explicite est de couler l'objet vers l'interface correcte et ensuite d'invoquer le membre sur cette interface. Mais une fois que vous avez fait le casting vers IConnection le IConnection.ConnectionString n'a pas de setter.

Donc il n'y a aucun moyen de invoquez cette méthode privée.

10voto

supercat Points 25534

Le problème est que lorsqu'un membre d'interface est déclaré explicitement, le compilateur génère un private avec un nom "imprononçable", et ne fournit aucun moyen par lequel le code - même au sein de la classe d'implémentation - de se référer à cette mise en œuvre.

En gros, quand on dit void IFoo.Moo() on dit que l'on ne veut pas définir un nom. Moo dans la portée de la classe ; par conséquent, le compilateur ne le fera pas. Pour que private set pour fonctionner, le membre devrait être un nom "prononçable", et le fait que le membre ait été explicitement implémenté est considéré comme une indication que l'on ne veut pas que le nom soit Moo .

En pratique, le remède ici est probablement le même que pour de nombreux autres cas où il est nécessaire d'avoir une implémentation d'interface dont le nom est prononçable, mais qui n'est pas exposée publiquement sous son nom : déclarer une implémentation d'interface qui ne fait rien d'autre que de chaîner à d'autres membres qui ont l'accessibilité appropriée, par exemple si les classes dérivées ne doivent pas pouvoir affecter la valeur la valeur :

private readonly int _foo = whatever;
public int IFOO.Foo { get {return _foo;}}

ou, si les classes dérivées doivent pouvoir l'affecter, soit

protected int _foo = whatever;
public int IFOO.Foo { get {return _foo;}}

ou

private int _foo = whatever;
protected virtual int setFoo(int value) { _foo = value; }
protected virtual int getFoo() { return _foo; }
public int IFOO.Foo { get {return getFoo();}}

En vb.net, les interfaces peuvent être implémentées en utilisant des membres de classe protégés, mais C# n'offre pas cette possibilité.

7voto

Magus Points 1038

Je pense que le cœur du problème est que l'interface ne possède que ce dont elle a besoin. Si ce n'est pas public, cela ne fait naturellement pas partie de l'interface. Lorsque vous implémentez explicitement l'interface, elle n'existe donc pas.

Dans le cas implicite, votre code s'adapte à l'interface, mais n'est pas totalement contraint par celle-ci. Vous pouvez ajouter d'autres éléments si nécessaire.

Obtenir des informations sur pourquoi il faudrait un concepteur de la langue pour vous répondre. Cela me semble pourtant logique : si elle ne fait pas partie de l'interface, vous ne pouvez pas l'implémenter/y accéder en tant que partie de l'interface.

4voto

GolezTrol Points 54531

La déclaration de propriété est une chose atomique contenant un getter et un setter. Elle doit correspondre à l'interface.

Si vous serait Si vous le permettez, il semble que le getter et le setter soient considérés comme des choses différentes. Dans ce cas, il n'y a pas non plus d'utilité à le limiter aux propriétés privées. Dans ce cas, l'interface indique simplement qu'il doit y avoir une propriété qui peut être lue, et vous êtes libre de la rendre inscriptible également.

Quoi qu'il en soit, apparemment c'est une décision de conception pour le rendre atomique.

4voto

Bas Brekelmans Points 13799

Peut-être que l'implémentation explicite de l'interface ne devrait pas être considérée comme faisant partie de la classe elle-même mais plutôt comme une sorte d'adaptateur de votre interface à la classe. Dans cette optique, considérez l'implémentation suivante :

public interface I {
    string S { get; set; }
}

class C : I {
    public C() {
        this.S = "Hello World"; 
        //compile error: explicit implementation not accessible
    }

    string I.S { get; set; }
}

Dans la classe C, la propriété S n'est même pas accessible en privé car il s'agit d'une implémentation explicite. Ne serait-ce pas une mauvaise conception en premier lieu que d'avoir une implémentation concrète d'un champ qui ne soit pas accessible par l'implémentation elle-même ?

De plus, dans votre exemple, la création d'un setter pour l'implémentation explicite ne serait jamais accessible ; puisque la propriété n'est accessible que lorsqu'elle est coulée dans l'attribut IConnection interface. Là, il n'y a qu'un getter.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X