Pourquoi le code C # suivant n'est-il pas autorisé:
public abstract class BaseClass
{
public abstract int Bar { get;}
}
public class ConcreteClass : BaseClass
{
public override int Bar
{
get { return 0; }
set {}
}
}
CS0546 'ConcreteClass.Bar.set': impossible de remplacer car 'BaseClass.Bar' n'a pas d'accesseur de jeu remplaçable
c#
.net
properties
getter-setter
ripper234
la source
la source
Foo
qui ne fait rien d'autre que envelopper uneGetFoo
méthode abstraite protégée . Un type dérivé abstrait peut masquer la propriété avec une propriété de lecture-écriture non virtuelle qui ne fait rien d'autre que envelopper laGetFoo
méthode susmentionnée avec uneSetFoo
méthode abstraite ; un type dérivé concret pourrait faire ce qui précède mais fournir des définitions pourGetFoo
etSetFoo
.Réponses:
Parce que l'auteur de Baseclass a explicitement déclaré que Bar doit être une propriété en lecture seule. Cela n'a aucun sens que les dérivations rompent ce contrat et le rendent en lecture-écriture.
Je suis avec Microsoft sur celui-ci.
Disons que je suis un nouveau programmeur à qui on a dit de coder avec la dérivation Baseclass. J'écris quelque chose qui suppose que Bar ne peut pas être écrit (puisque la classe de base déclare explicitement qu'il s'agit d'une propriété get only). Maintenant, avec votre dérivation, mon code peut casser. par exemple
Puisque Bar ne peut pas être défini selon l'interface BaseClass, BarProvider suppose que la mise en cache est une chose sûre à faire - Puisque Bar ne peut pas être modifié. Mais si set était possible dans une dérivation, cette classe pourrait servir des valeurs périmées si quelqu'un modifiait la propriété Bar de l'objet _source en externe. Le point étant `` Soyez ouvert, évitez de faire des choses sournoises et de surprendre les gens ''
Mise à jour : Ilya Ryzhenkov demande "Pourquoi les interfaces ne fonctionnent-elles pas selon les mêmes règles alors?" Hmm ... cela devient plus confus quand j'y pense.
Une interface est un contrat qui dit «s'attendre à ce qu'une implémentation ait une propriété de lecture nommée Bar». Personnellement, je suis beaucoup moins susceptible de faire cette hypothèse de lecture seule si j'ai vu une interface. Quand je vois une propriété en lecture seule sur une interface, je la lis comme «Toute implémentation exposerait cet attribut Bar» ... sur une classe de base, il clique comme «La barre est une propriété en lecture seule». Bien sûr, techniquement, vous ne rompez pas le contrat ... vous faites plus. Donc vous avez raison dans un sens ... Je terminerais en disant: «Faites en sorte qu'il soit aussi difficile que possible que des malentendus surgissent».
la source
InvalidOperationException("This instance is read-only")
.Je pense que la raison principale est simplement que la syntaxe est trop explicite pour que cela fonctionne d'une autre manière. Ce code:
est assez explicite que le
get
et leset
sont des remplacements. Il n'y a passet
dans la classe de base, donc 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 non plus remplacer un setter.Vous pourriez dire que le compilateur devrait deviner votre intention et n'appliquer le remplacement qu'à la méthode qui peut être surchargée (c'est-à-dire le getter dans ce cas), mais cela va à l'encontre de l'un des principes de conception C # - que le compilateur ne doit pas deviner vos intentions , car il peut deviner mal sans que vous le sachiez.
Je pense que la syntaxe suivante pourrait faire l'affaire, mais comme Eric Lippert n'arrête pas de le dire, mettre en œuvre même une fonctionnalité mineure comme celle-ci représente toujours un effort majeur ...
ou, pour les propriétés auto-implémentées,
la source
Je suis tombé sur le même problème aujourd'hui et je pense avoir une raison très valable de vouloir cela.
Tout d'abord, j'aimerais dire que le fait d'avoir une propriété get-only ne se traduit pas nécessairement en lecture seule. Je l'interprète comme "A partir de cette interface / abtract, vous pouvez obtenir cette valeur", cela ne signifie pas qu'une implémentation de cette interface / classe abstraite n'aura pas besoin de l'utilisateur / du programme pour définir cette valeur explicitement. Les classes abstraites servent à implémenter une partie des fonctionnalités nécessaires. Je ne vois absolument aucune raison pour laquelle une classe héritée ne pourrait pas ajouter un setter sans violer aucun contrat.
Ce qui suit est un exemple simplifié de ce dont j'avais besoin aujourd'hui. J'ai fini par devoir ajouter un setter dans mon interface juste pour contourner cela. La raison de l'ajout du setter et de ne pas ajouter, disons, une méthode SetProp est qu'une implémentation particulière de l'interface a utilisé DataContract / DataMember pour la sérialisation de Prop, ce qui aurait été rendu inutilement compliqué si j'avais dû ajouter une autre propriété juste à cette fin de sérialisation.
Je sais que ce n'est qu'un article "mes 2 cents", mais je pense qu'avec l'affiche originale et essayer de rationaliser que c'est une bonne chose me semble étrange, d'autant plus que les mêmes limitations ne s'appliquent pas lors de l'héritage direct d'un interface.
De plus, la mention d'utiliser new au lieu de override ne s'applique pas ici, cela ne fonctionne tout simplement pas et même si c'était le cas, cela ne vous donnerait pas le résultat souhaité, à savoir un getter virtuel tel que décrit par l'interface.
la source
C'est possible
tl; dr - Vous pouvez remplacer une méthode get-only avec un setter si vous le souhaitez. C'est fondamentalement juste:
Créez une
new
propriété qui a à la fois unget
et un enset
utilisant le même nom.Si vous ne faites rien d'autre, l'ancienne
get
méthode sera toujours appelée lorsque la classe dérivée sera appelée via son type de base. Pour résoudre ce problème, ajoutez uneabstract
couche intermédiaire qui utiliseoverride
l'ancienneget
méthode pour la forcer à renvoyer leget
résultat de la nouvelle méthode.Cela nous permet de remplacer les propriétés par
get
/set
même s'il en manquait un dans leur définition de base.En prime, vous pouvez également modifier le type de retour si vous le souhaitez.
Si la définition de base était
get
-only, vous pouvez utiliser un type de retour plus dérivé.Si la définition de base était
set
-only, vous pouvez utiliser un type de retour moins dérivé.Si la définition de base était déjà
get
/set
, alors:vous pouvez utiliser un type de retour plus dérivé si vous le faites
set
-only;vous pouvez utiliser un type de retour moins dérivé si vous le
get
définissez -only.Dans tous les cas, vous pouvez conserver le même type de retour si vous le souhaitez. Les exemples ci-dessous utilisent le même type de retour pour plus de simplicité.
Situation:
get
propriété préexistante uniquementVous avez une structure de classe que vous ne pouvez pas modifier. C'est peut-être juste une classe ou c'est un arbre d'héritage préexistant. Dans tous les cas, vous souhaitez ajouter une
set
méthode à une propriété, mais vous ne pouvez pas.Problème: ne peut pas
override
leget
-seulement avecget
/set
Vous voulez
override
avec une propriétéget
/set
, mais elle ne se compilera pas.Solution: utilisez une
abstract
couche intermédiaireBien que vous ne puissiez pas directement
override
avec une propriétéget
/set
, vous pouvez :Créez une propriété
new
get
/set
avec le même nom.override
l'ancienneget
méthode avec un accesseur à la nouvelleget
méthode pour assurer la cohérence.Donc, vous écrivez d'abord la
abstract
couche intermédiaire:Ensuite, vous écrivez la classe qui ne serait pas compilée plus tôt. Il compilera cette fois parce que vous n'êtes pas en fait
override
laget
propriété -only; à la place, vous le remplacez à l'aide dunew
mot - clé.Résultat: tout fonctionne!
De toute évidence, cela fonctionne comme prévu
D
.Tout fonctionne toujours comme prévu lors de la visualisation
D
comme l'une de ses classes de base, par exempleA
ouB
. Mais, la raison pour laquelle cela fonctionne pourrait être un peu moins évidente.Cependant, la définition de la classe de base de
get
est toujours remplacée par la définition de la classe dérivée deget
, elle est donc toujours totalement cohérente.Discussion
Cette méthode vous permet d'ajouter des
set
méthodes auxget
propriétés -only. Vous pouvez également l'utiliser pour faire des choses comme:Changez n'importe quelle propriété en une propriété
get
-only,set
-only ouget
-and-set
, indépendamment de ce qu'elle était dans une classe de base.Modifiez le type de retour d'une méthode dans les classes dérivées.
Les principaux inconvénients sont qu'il y a plus de codage à faire et un supplément
abstract class
dans l'arbre d'héritage. Cela peut être un peu gênant avec les constructeurs qui prennent des paramètres car ceux-ci doivent être copiés / collés dans la couche intermédiaire.la source
Je suis d'accord que ne pas pouvoir remplacer un getter dans un type dérivé est un anti-pattern. Lecture seule spécifie le manque d'implémentation, pas un contrat d'une pure fonctionnelle (implicite par la réponse du vote le plus élevé).
Je soupçonne que Microsoft avait cette limitation soit parce que la même idée fausse a été promue, soit peut-être à cause d'une simplification de la grammaire; cependant, maintenant que la portée peut être appliquée pour obtenir ou définir individuellement, peut-être pouvons-nous espérer que le remplacement le sera également.
L'idée fausse indiquée par la réponse du vote le plus élevé, selon laquelle une propriété en lecture seule devrait d'une manière ou d'une autre être plus «pure» qu'une propriété en lecture / écriture est ridicule. Regardez simplement de nombreuses propriétés courantes en lecture seule dans le framework; la valeur n'est pas une constante / purement fonctionnelle; par exemple, DateTime.Now est en lecture seule, mais tout sauf une valeur fonctionnelle pure. Une tentative de «mettre en cache» une valeur d'une propriété en lecture seule en supposant qu'elle retournera la même valeur la prochaine fois est risquée.
Dans tous les cas, j'ai utilisé l'une des stratégies suivantes pour surmonter cette limitation; les deux sont loin d'être parfaits, mais vous permettront de boiter au-delà de cette carence linguistique:
la source
Vous pourriez peut-être contourner le problème en créant une nouvelle propriété:
la source
Je peux comprendre tous vos points, mais effectivement, les propriétés automatiques de C # 3.0 deviennent inutiles dans ce cas.
Vous ne pouvez rien faire de tel:
OMI, C # ne devrait pas restreindre de tels scénarios. Il est de la responsabilité du développeur de l'utiliser en conséquence.
la source
Le problème est que pour une raison quelconque, Microsoft a décidé qu'il devrait y avoir trois types de propriétés distincts: lecture seule, écriture seule et lecture-écriture, dont un seul peut exister avec une signature donnée dans un contexte donné; les propriétés ne peuvent être remplacées que par des propriétés déclarées de manière identique. Pour faire ce que vous voulez, il serait nécessaire de créer deux propriétés avec le même nom et la même signature - dont l'une était en lecture seule et l'autre en lecture-écriture.
Personnellement, je souhaite que tout le concept de "propriétés" puisse être aboli, sauf que la syntaxe de propriété pourrait être utilisée comme sucre syntaxique pour appeler les méthodes "get" et "set". Cela faciliterait non seulement l'option 'ajouter un ensemble', mais permettrait également à 'get' de renvoyer un type différent de 'set'. Bien qu'une telle capacité ne soit pas très souvent utilisée, il peut parfois être utile qu'une méthode «get» renvoie un objet wrapper tandis que le «set» peut accepter soit un wrapper, soit des données réelles.
la source
Voici une solution de contournement pour y parvenir en utilisant Reflection:
De cette façon, nous pouvons définir la valeur d'objet sur une propriété en lecture seule. Cela ne fonctionnera peut-être pas dans tous les scénarios!
la source
Vous devez modifier le titre de votre question afin de préciser que votre question concerne uniquement le remplacement d'une propriété abstraite ou que votre question concerne généralement le remplacement de la propriété get-only d'une classe.
Si le premier (remplaçant une propriété abstraite)
Ce code est inutile. Une classe de base seule ne devrait pas vous dire que vous êtes obligé de remplacer une propriété Get-Only (peut-être une interface). Une classe de base fournit des fonctionnalités communes qui peuvent nécessiter une entrée spécifique d'une classe d'implémentation. Par conséquent, la fonctionnalité commune peut appeler des propriétés ou des méthodes abstraites. Dans le cas donné, les méthodes de fonctionnalité courantes devraient vous demander de remplacer une méthode abstraite telle que:
Mais si vous n'avez aucun contrôle sur cela et que la fonctionnalité de la classe de base lit à partir de sa propre propriété publique (bizarre), alors faites simplement ceci:
Je tiens à souligner le commentaire (étrange): je dirais qu'une meilleure pratique est pour une classe de ne pas utiliser ses propres propriétés publiques, mais d'utiliser ses champs privés / protégés lorsqu'ils existent. C'est donc un meilleur modèle:
Si ce dernier (remplaçant la propriété get-only d'une classe)
Chaque propriété non abstraite a un setter. Sinon, c'est inutile et vous ne devriez pas vous en soucier. Microsoft n'est pas obligé de vous autoriser à faire ce que vous voulez. La raison étant: le setter existe sous une forme ou une autre, et vous pouvez accomplir ce que vous voulez Veerryy facilement.
La classe de base, ou une classe où vous pouvez lire une propriété avec
{get;}
, a SOME sorte de poseur de cette propriété exposée. Les métadonnées ressembleront à ceci:Mais la mise en œuvre aura deux extrémités du spectre de la complexité:
Le moins complexe:
Le plus complexe:
Mon point étant, de toute façon, vous avez le passeur exposé. Dans votre cas, vous voudrez peut-être remplacer
int Bar
parce que vous ne voulez pas que la classe de base le gère, que vous n'avez pas accès pour examiner comment elle le gère ou que vous avez été chargé de pirater du code très rapidement contre votre volonté. .Dans les derniers et les anciens (Conclusion)
En bref: il n'est pas nécessaire que Microsoft change quoi que ce soit. Vous pouvez choisir comment votre classe d'implémentation est configurée et, sans le constructeur, utiliser tout ou aucune de la classe de base.
la source
Solution pour seulement un petit sous-ensemble de cas d'utilisation, mais néanmoins: en C # 6.0, le setter "lecture seule" est automatiquement ajouté pour les propriétés écrasées en lecture seule.
la source
Parce que cela briserait le concept d'encapsulation et de dissimulation de la mise en œuvre. Considérez le cas lorsque vous créez une classe, l'expédiez, puis le consommateur de votre classe se rend capable de définir une propriété pour laquelle vous ne fournissez à l'origine qu'un getter. Cela perturberait efficacement tous les invariants de votre classe dont vous pouvez compter dans votre implémentation.
la source
Ce n'est pas impossible. Il vous suffit d'utiliser le mot-clé «nouveau» dans votre propriété. Par exemple,
Il semble que cette approche satisfait les deux côtés de cette discussion. L'utilisation de "new" rompt le contrat entre l'implémentation de la classe de base et l'implémentation de la sous-classe. Ceci est nécessaire lorsqu'une classe peut avoir plusieurs contrats (via l'interface ou la classe de base).
J'espère que cela t'aides
la source
new
ne remplace plus celle de base virtuelle. En particulier, si la propriété de base est abstraite , comme dans le message d'origine, il est impossible d'utiliser lenew
mot - clé dans une sous-classe non abstraite car vous devez remplacer tous les membres abstraits.Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)
vous donne 5,Console.WriteLine(b.BaseProperty)
vous donne 0, et lorsque votre successeur doit déboguer cela, vous feriez mieux de vous déplacer loin, très loin.Une propriété en lecture seule dans la classe de base indique que cette propriété représente une valeur qui peut toujours être déterminée à partir de la classe (par exemple une valeur enum correspondant au contexte (db-) d'un objet). La responsabilité de déterminer la valeur reste donc au sein de la classe.
L'ajout d'un setter provoquerait ici un problème gênant: une erreur de validation devrait se produire si vous définissez la valeur sur autre chose que la seule valeur possible qu'elle possède déjà.
Cependant, les règles ont souvent des exceptions. Il est très bien possible que, par exemple, dans une classe dérivée, le contexte réduise les valeurs d'énumération possibles à 3 sur 10, mais l'utilisateur de cet objet doit encore décider lequel est correct. La classe dérivée doit déléguer la responsabilité de déterminer la valeur à l'utilisateur de cet objet. Il est important de comprendre que l'utilisateur de cet objet doit être bien conscient de cette exception et assumer la responsabilité de définir la valeur correcte.
Ma solution dans ce genre de situation serait de laisser la propriété en lecture seule et d'ajouter une nouvelle propriété en lecture-écriture à la classe dérivée pour prendre en charge l'exception. Le remplacement de la propriété d'origine renverra simplement la valeur de la nouvelle propriété. La nouvelle propriété peut avoir un nom approprié indiquant correctement le contexte de cette exception.
Cela confirme également la remarque valable: "faites en sorte qu'il soit aussi difficile que possible que des malentendus surgissent" par Gishu.
la source
ReadableMatrix
classe (avec une propriété indexée en lecture seule à deux arguments) ayant des sousImmutableMatrix
- types etMutableMatrix
. Un code qui n'a besoin que de lire une matrice, et ne se soucie pas de savoir s'il pourrait changer quelque temps dans le futur, pourrait accepter un paramètre de typeReadableMatrix
et être parfaitement satisfait des sous-types mutables ou immuables.List<T>.Count
fait: il ne peut pas être attribué à, mais cela ne signifie pas qu'il est "en lecture seule" dans la mesure où il ne peut pas être modifié par les utilisateurs de la classe. Il peut très bien être modifié, bien qu'indirectement, en ajoutant et en supprimant des éléments de liste.Parce qu'au niveau IL, une propriété en lecture / écriture se traduit en deux méthodes (getter et setter).
Lors du remplacement, vous devez continuer à prendre en charge l'interface sous-jacente. Si vous pouviez ajouter un setter, vous ajouteriez effectivement une nouvelle méthode, qui resterait invisible pour le monde extérieur, en ce qui concerne l'interface de vos classes.
Certes, l'ajout d'une nouvelle méthode ne briserait pas la compatibilité en soi, mais comme elle resterait cachée, la décision de l'interdire est parfaitement logique.
la source
new
place deoverride
.new
avec vos ajouts.Parce qu'une classe qui a une propriété en lecture seule (pas de setter) a probablement une bonne raison pour cela. Il se peut qu'il n'y ait pas de banque de données sous-jacente, par exemple. Le fait de vous permettre de créer un setter rompt le contrat établi par la classe. C'est juste une mauvaise POO.
la source