165 votes

Pourquoi est-il impossible de substituer une propriété d’accesseur Get-only et ajouter un setter ?

Pourquoi pensez-vous (ou, pourquoi est-il bon que) Microsoft a choisi de ne pas permettre :

47voto

romkyns Points 17295

Je pense que la principale raison est tout simplement que la syntaxe est trop explicite pour que cela fonctionne d'une autre façon. Ce code:

public override int MyProperty { get { ... } set { ... } }

est tout à fait explicite que les deux get et de la set sont des remplacements. Il n'y a pas d' set dans la classe de base, de sorte que le compilateur se plaint. Tout comme vous ne pouvez pas remplacer une méthode qui n'est pas définie dans la classe de base, vous ne pouvez pas remplacer un setter.

On pourrait dire que le compilateur doit deviner votre intention et s'appliquent uniquement le remplacement de la méthode qui peut être remplacée (c'est à dire de la lecture dans ce cas), mais cela va à l'encontre de l'un des C# principes de conception - que le compilateur ne doit pas deviner vos intentions, car il peut deviner de mal sans le savoir.

Je pense que la syntaxe suivante pourrait faire bien, mais comme Eric Lippert arrête pas de dire, la mise en œuvre de même une fonctionnalité mineure, comme c'est encore l'une des principales montant de l'effort...

public int MyProperty
{
    override get { ... }
    set { ... }
}

ou, pour autoimplemented propriétés,

public int MyProperty { override get; set; }

33voto

Trouvé un moyen d'obtenir la fonctionnalité désirée:

public abstract class A
{
    public abstract int X { get; }
}
public class B : A
{
    public override int X { get { return 0; } }
}
/*public class C : B  //class you want to write but won't compile because can't override with setter
{
    private int _x;
    public override int X { get { return _x; } set { _x = value; } }
}*/
public abstract class C : B  //abstract intermediate layer
{
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}
public class D : C  //class you want to write that will compile
{
    private int _x;
    protected sealed override int XGetter { get { return this.X; } }
    public new virtual int X { get { return this._x; } set { this._x = value; } }
}

Donc au lieu d'écrire votre nouvelle classe C : B, ce qui ne vous permettra pas de remplacer la méthode, écrire D : C : BD est la classe que vous voulais écrire et C est un résumé intermédiaire qui scelle loin la seule limitation sur la base de la propriété. D pouvez remplacer une propriété avec une nouvelle méthode virtuelle où le nouveau getter appelle la base de la méthode de lecture.

L'inconvénient de cette approche est qu'elle nécessite un supplément de classe abstraite construite dans l'arbre d'héritage.

23voto

JJJ Points 149

Je suis tombé sur le même problème aujourd'hui et je pense avoir un très raison valable de le vouloir.

J'aimerais d'abord valoir que le fait d'avoir une seule propriété ne traduit pas nécessairement en lecture seule. - Je l'interpréter comme "à Partir de cette interface/abtract vous pouvez obtenir cette valeur", cela ne signifie pas que l'implémentation de cette interface/classe abstraite n'aurez pas besoin de l'utilisateur/programme pour définir cette valeur de manière explicite. Les classes abstraites servir les fins de l'exécution d'une partie de la fonctionnalité nécessaire. Je ne vois absolument aucune raison pourquoi une classe héritée ne pouvais pas ajouter un setter sans violer les contrats.

Ce qui suit est un exemple simplifié de ce dont j'avais besoin aujourd'hui. J'ai fini par avoir à ajouter un setter dans ma classe abstraite juste pour obtenir autour de cette. La raison de l'ajout de l'incubateur et de ne pas ajouter, disons, un SetProp méthode est que l'un particulier de la mise en œuvre de l'interface utilisée DataContract/DataMember pour la sérialisation de l'Hélice, ce qui aurait été fait inutilement compliqué si je devais ajouter une autre propriété dans le but de sérialisation.

    interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

Je sais que c'est juste un "my 2 cents", le post, mais je me sens à l'affiche originale et en essayant de rationaliser que c'est une bonne chose me semble bizarre, d'autant que, les mêmes limitations ne s'appliquent pas lorsque héritant directement à partir d'une interface.

Aussi la mention sur l'utilisation des nouvelles au lieu de dérogation ne s'applique pas ici, c'est tout simplement ne fonctionne pas et même si elle l'était, il ne serait pas vous donner le résultat voulu, à savoir un virtuel de lecture tel que décrit par l'interface.

12voto

T.Tobler Points 31

Je suis d'accord de ne pas être en mesure de remplacer un getter dans un type dérivé est un anti-pattern. En lecture Seule indique le manque de mise en œuvre, et non un contrat de un pur fonctionnel (implicite par le top vote réponse).

Je soupçonne Microsoft avait cette limitation soit parce que la même idée fausse a été promu, ou peut-être en raison de la simplification de la grammaire; mais, maintenant que la portée peut être appliquée pour obtenir ou définir individuellement, nous pouvons peut-être espérer modifier peut-être trop.

L'idée fausse indiqué par le top vote réponse, qu'une propriété en lecture seule doit en quelque sorte être plus "pure" que d'une propriété de lecture/écriture est ridicule. Suffit de regarder de nombreuses propriétés en lecture seulement dans le cadre, la valeur n'est pas une constante / purement fonctionnel, par exemple, de type DateTime.Maintenant est en lecture seule, mais rien, mais une pure valeur fonctionnelle. Une tentative de "cache" de la valeur d'une propriété en lecture seule en supposant qu'il sera de retour la même valeur prochaine fois, c'est risqué.

En tout cas, j'ai utilisé l'une des méthodes suivantes pour contourner cette limitation, les deux sont moins que parfait, mais vous permettra de mou au-delà de cette langue carence:

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }

-8voto

Gishu Points 59012

Parce que l'auteur de Baseclass a explicitement déclaré que la Barre doit être une propriété en lecture seule. Il ne fait pas de sens pour les dérivations de rompre le présent contrat et faire lire-écrire.

Je suis avec Microsoft sur ce point.
Disons que je suis un nouveau programmeur qui a été dit de code par rapport à la Baseclass de dérivation. j'écris quelque chose qui suppose que la Barre ne peut pas être écrite (depuis le Baseclass stipule explicitement que c'est une seule propriété). Maintenant, avec votre dérivation, mon code ne peut briser. par exemple

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

Depuis la Barre ne peut pas être défini que par la BaseClass interface, BarProvider suppose que la mise en cache est une chose sûre à faire - Depuis la Barre ne peut pas être modifié. Mais si le jeu a été possible dans une dérivation, cette classe pourrait être au service de valeurs obsolètes si quelqu'un a modifié la source de l'objet de la Barre de propriété à l'extérieur. Le point de l'être"Être Ouvert, éviter de faire sournois des choses et des gens surprenants*'

Mise à jour: Ilya Ryzhenkov demande " Pourquoi ne pas les interfaces jouent selon les mêmes règles alors?" Hmm.. cela devient boueux que je pense à ce sujet.
Une interface est un contrat qui dit " s'attendre à une mise en œuvre à avoir une lecture bien nommé Bar.' Personnellement , je suis beaucoup moins susceptibles de rendre cette hypothèse de lecture seule si j'ai vu une Interface. Quand je vois une seule propriété sur une interface, je l'ai lu comme "Toute mise en œuvre d'exposer cet attribut Bar" ... sur une base de classe, il clique sur 'Bar est une propriété en lecture seule'. Bien sûr, techniquement, vous n'êtes pas rompre le contrat.. vous êtes en train de faire plus. Oui tu as raison dans un sens.. je voudrais terminer en disant qu'il est aussi dur que possible pour malentendus surgissent'.

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