2014-09-10 1 views
4

Je suis dans une situation où je crée un objet appelé EntryEvent à partir des données que je reçois. Ces données doivent être analysées. L'événement de base est censé lancer l'analyse des données reçues par le constructeur et données à l'objet. Le sous-type sait comment analyser cet ensemble de données spécifique. Maintenant, lors de la compilation de ce code, j'obtiens l'avertissement CA2214, qui contient une chaîne d'appel à une méthode virtuelle. Bien qu'il puisse être mauvais d'avoir des conséquences imprévues, je ne sais pas comment obtenir le comportement requis: Analyser l'événement reçu sans avoir à appeler une méthode "Parse" supplémentaire de l'extérieur.Surpression ou évitement Avertissement CA2214

Le code en question est:

public abstract class BaseEvent 
{ 
    protected BaseEvent(object stuff) 
    { 
     this.ParseEvent(); 
    } 

    protected abstract void ParseEvent(); 
} 

public class EntryEvent : BaseEvent 
{ 
    public EntryEvent(object stuff) 
     : base(stuff) 
    { 
    } 

    protected override void ParseEvent() 
    { 
     // Parse event 
    } 
} 
+1

Pouvez-vous nous donner plus de contexte?Il y a différentes façons d'aborder cela qui ne causeraient pas le problème, mais quelle approche est la meilleure dépendra du contexte. Est-il raisonnable d'imaginer une sous-classe qui * n'a pas besoin de remplacer ParseEvent? Est-ce que chaque sous-classe pourrait avoir une méthode statique qui a été donnée aux données, l'analyser, puis appeler les constructeurs qui ont pris les données déjà analysées? –

+0

Dans le code de votre question, 'ParseEvent()' pourrait être non virtuel, défini uniquement pour 'EntryEvent', et appelé directement depuis le constructeur de' EntryEvent'. Si ce n'est pas possible pour votre code réel, pouvez-vous mettre à jour votre question pour mieux illustrer le scénario auquel vous avez affaire? – hvd

+0

@JonSkeet L'événement provient d'une source externe. Chaque sous-classe doit être prioritaire, car chaque sous-classe doit effectuer son propre analyse. La classe de base contient les données générales fournies par chaque événement. Ce que j'avais l'intention était de forcer l'exécutant d'un événement particulier à implémenter l'analyseur, sans le forcer à l'appeler. Ce que je voulais éviter, c'était de donner à l'exécutant (même si c'était moi) la possibilité de nommer la méthode d'analyse au hasard, que ce soit statique ou non, mais peut-être que je l'analyse trop. –

Répondre

2

Selon MSDN (l'accent est à moi):

Lorsqu'une méthode virtuelle est appelée, le type réel qui exécute la méthode n'est pas sélectionnée jusqu'à ce que temps d'exécution. Lorsqu'un constructeur appelle une méthode virtuelle, , il est possible que le constructeur de l'instance qui appelle la méthode n'ait pas été exécuté.

Donc, à mon avis, vous avez ces options (au moins):

1) Ne pas désactiver cet avertissement, mais supprimer ce message pour votre classe spécifique (s) documentant son comportement prévu (en supposant que vous prenez supplémentaire soin de faire face à un tel scénario). Ce n'est pas si grave si elle est limitée à quelques classes dans un environnement très contrôlé (après tout ... les avertissements ne sont pas des erreurs et ils peuvent être ignorés).

2) Supprimer cet appel de méthode virtuelle du constructeur de la classe de base mais laisser la déclaration de la méthode abstract à cet endroit. Les développeurs devront implémenter une telle méthode et pour l'appeler dans le constructeur, ils devront marquer leurs classes comme sealed. Enfin, ajoutez quelque part dans la documentation de classe/méthode que cette méthode doit être appelée dans leur constructeur et que leur classe doit être sealed pour le faire. Ils peuvent oublier appeler mais vous pouvez ajouter (pour les builds DEBUG) une vérification lorsque des propriétés ou méthodes sont accédées (par exemple en forçant, en tant que partie de l'interface de classe, à définir un indicateur spécifique). S'ils oublient de mettre le drapeau ou oublient d'appeler la méthode alors une exception sera levée ("Cet objet n'a pas été construit, ParseEvent() doit être appelé dans le constructeur des classes dérivées."). Je n'aime pas beaucoup cette méthode parce qu'elle ajoute de la complexité mais si votre hiérarchie de classe est trop grande (alors vous ne pouvez pas utiliser # 1) ou l'initialisation paresseuse (décrite dans # 3) n'est pas applicable alors il peut s'agir d'une solution fonctionnant. Je considérerais également pour changer la conception pour introduire une méthode d'usine qui appellera ParseEvent() pour chaque objet entièrement construit.

3) Changez un peu votre conception: reportez l'analyse au besoin. Par exemple:

public abstract class BaseEvent 
{ 
    public DateTime TimeStamp 
    { 
     get 
     { 
      if (_timestamp == null) 
       ParseEvent(); 

      return _timestamp.Value; 
     } 
     protected set { _timestamp = value; } 
    } 

    protected BaseEvent(object stuff) 
    { 
    } 

    protected abstract void ParseEvent(); 

    private DateTime? _timestamp; 
} 

Le dernier exemple est uniquement à des fins d'illustration, vous pouvez utiliser Lazy<T> faire même tâche dans une plus coincise, de manière claire et sûre fil. Bien sûr, en réalité, vous aurez plus de champs/propriétés et probablement l'analyse fournira toutes les valeurs en un coup (alors vous avez juste besoin d'un drapeau, pas besoin de Nullable/valeur spéciale sur chaque champ) C'est l'approche que je préfère, même si c'est plus bavard.

+0

Merci. comme pour 3), le chargement paresseux n'est pas une option, car les données peuvent être indisponibles à partir de la source au moment où il est nécessaire. Bien sûr, je voudrais éviter 1) car je vois les problèmes potentiels. 2) semble être un compromis valide, en particulier la combinaison de la documentation avec la méthode abstraite qu'un développeur doit mettre en œuvre. Je voulais juste éviter que le développeur d'une sous-classe puisse oublier ou ignorer quelque chose. –

+0

@private_meta avec 2) le développeur peut oublier d'appeler ParseEvent() dans son constructeur de classes. Sa faute, mais vous pouvez faire quelques vérifications lors de l'accès aux propriétés (au moins dans les builds DEBUG). Peut-être une classe d'usine et une méthode Parse() à appeler par cela ...) –

Questions connexes