Question

J'ai été demandé de maintenir une certaine non-as-héritage-en-I-serait comme code, et il est criblé de directives du compilateur, ce qui en fait à peu près illisible et presque aussi maintenable. Exemple:

#if CONDITION_1
        protected override void BeforeAdd(LogEntity entity)
#else
        protected override void BeforeAdd(AbstractBusinessEntity entity)
#endif
        {
#if CONDITON_1
            entity.DateTimeInsert = DateTime.Now;
#else
            ((LogEntity) entity).DateTimeInsert = DateTime.Now;
#endif
            base.BeforeAdd(entity);
        }

directives de using sont encore plus jolie:

#if CONDITION_1
using CompanyName.Configuration;
#endif

#if CONDITION_2||CONDITION_1
using CompanyName.Data;
using CompanyName.Data.SqlBuilders;
#else
using CompanyName.Legacy.Database;
using CompanyName.Legacy.Database.SQLBuilders;
using CompanyName.Legacy.Database.SQLBuilders.parameterTypes;
#endif

Je pensais que je donnerais ConditionalAttribute un aller, mais qui ne sera pas tout à fait le travail dans cette situation

Est-il possible que je peux me sortir de ce cauchemar de directive du compilateur?

Le code est compilé .NET 3.5.

Mise à jour: Oded suggérant la suppression des répondit directives du compilateur autour de la méthode BeforeAdd la surcharge ainsi. Malheureusement, cela ne fonctionnera pas car les deux méthodes sont censés être une classe de remplaçant AbstractBusiness qui fournit deux implémentations différentes selon lequel les ensembles finissent par être inclus:

protected virtual void BeforeAdd(TEntity entity) {}

ou

protected virtual void BeforeAdd(AbstractBusinessEntity entity) {}

Ce code obtient ses dépendances à partir d'un ensemble de bibliothèques de société a créé un certain temps dans le passé et ont été « mise à niveau » depuis. Ils ont maintenant 4 différentes versions de cet ensemble de bibliothèques avec entrer en collision et namespaces implémentations différentes. Au nom de la « compatibilité ascendante » avec des applications qui utilisent les anciennes versions (très).


Conclusion

J'ai fini par choisir @ réponse de Oded parce qu'il est le plus logique comme une approche générale ( Kiss et tout cela). Je ne pouvais pas l'utiliser dans ce cas bien; ce que vous voyez ici est que la pointe de l'iceberg. Je ne voudrais pas K.I.S.S. ce code si elle m'a payé.

Était-ce utile?

La solution

Dans le premier cas, il semble que vous pourriez simplement avoir plusieurs surcharges de la méthode au lieu de cette construction. Les surcharges doit prendre soin des choses à ce moment.

Dans le second cas (en utilisant les directives) - vous pouvez créer un alias pour certaines des directives et comprennent tous, en utilisant l'alias en cas de besoin. Qu'advient-il lorsque tous les espaces de noms sont inclus? Toutes les collisions de nom?

Autres conseils

Je prétendre que le problème n'est pas dans cette classe. Cette classe est juste un symptôme. Le problème est dans la classe de base qui vous appelle BeforeAdd. Si vous pouvez factoriser là, alors vous aurez pas besoin des la compilation conditionnelle.

Si vous avez des noms contradictoires et les espaces de noms, vous pouvez travailler autour de ce avec le mot clé en utilisant (et non l'un pour les assemblées).

Vous pouvez faire quelque chose comme

using LegacyLogEntity = Some.Fully.Qualified.Namespace.LogEntity;
using SomeOtherLogEntity = Some.Other.Fully.Qualified.Namespace.CurrentLogEntity;

// ..
LegacyLogEntity entity = new LegacyLogEntity();

Je pense aussi que le problème est dans la classe de base, et non pas dans cette classe, en tant que tel.

Dans ce cas, vous pouvez contourner ce non-sens en utilisant soit l'adaptation ou l'interface.

Je ne sais pas ce que l'autre classe est appelée, mais disons que ce qu'on appelle un EntityAggregator.

public interface IEntity {
    DateTime InsertionTime { get; set; }
}

puis dans votre classe de base aggrégateur:

protected virtual void BeforeAdd(IEntity entity)
{ // whatever
}

puis dans votre sous-classe:

protected override void BeforeAdd(IEntity entity)
{
    entity.DateTime = DateTime.Now;
    base.BeforeAdd(entity);
}

Maintenant, vous pouvez adapter les autres objets à IEntity en mettant en œuvre cette interface.

Quand je regarde ce code, il me semble aussi que peut-être vous emploierez des événements au lieu de ce code.

Maintenant, si vous parlez de multiples compilation d'utilisation, où le code est compilé en deux endroits séparés dans deux conditions différentes, alors vous pouvez le faire avec plus de grâce à l'aide des classes partielles.

Vous isoler le code condition_1 en quelque chose comme ceci:

// in file WhateverYourClassIs.condition1.cs
#if !CONDITION_1
#error this file should never be included in a build WITHOUT CONDITION_1 set
#endif

public partial class WhateverYourClassIs {
    protected override void BeforeAdd(LogEntity entity) {
        entity.DateTimeInsert = DateTime.Now;
        base.BeforeAdd(entity);
    }
}

// in file WhateverYourClassIs.NotCondition1.cs

#if CONDITION_1
#error this file should never be included in a build WITH CONDITION_1 set
#endif

public partial class WhateverYourClassIs {
    protected override void BeforeAdd(AbstractBusinessEntity entity) {
        ((LogEntity)entity).DateTimeInsert = DateTime.Now;
        base.BeforeAdd(entity);
    }
}

Je ne aime pas cela dans ce cas, à cause de la répétition de code. Vous pouvez aider à cela avec l'utilisation du mot-clé en utilisant:

#if CONDITION_1
using MyAbstractBusinessEntity = LogEntity;
#else
using MyAbstractBusinessEntity = AbstractBusinessEntity;
#endif

// ...

protected override void BeforeAdd(MyAbstractBusinessEntity entity)
{
    // in CONDITION_1, the case is a no-op
    ((LogEntity)entity).DateTimeInsert = DateTime.Now;
    base.BeforeAdd(entity);
}

D'après ce que je vois, il semble que le développeur d'origine n'a pas de sens de l'héritage et le polymorphisme. Il est un peu difficile de dire à partir du code, mais il semble LogEntity part et AbstractBusinessEntity propriétés communes. Y at-il un modèle d'héritage ou sont-ils deux classes sans aucun rapport? Si elles ne sont pas liés, pourriez-vous créer un modèle d'héritage ou d'une interface, ils peuvent à la fois mettre en œuvre? Il peut être utile si vous les classes de coller.

Longue histoire courte, je ne perdrai pas mon temps à essayer de travailler avec ce code dans sa forme actuelle. Je trouve un moyen d'éliminer les directives du compilateur, à tout prix. Il ne semble pas être complètement non réparable, mais il peut prendre un certain effort.

Je ne sais pas si elle est pratique, mais ce que je ferais serait de créer des succursales dans mes DVCS, Mercurial, pour gérer cela.

J'aurais 2 branches en jeu, et une 3ème temporairement pendant que je corrige des bugs / code add qui est commun.

Voici comment je créerais les versions initiales:

              5---6---7         <-- type 1 of library
             /
1---2---3---4
             \
              8---9--10         <-- type 2 of library

Pour les bugs fix dans un seul d'entre eux:

              5---6---7--11     <-- bugfix or change only to type 1
             /
1---2---3---4
             \
              8---9--10

Pour corriger les bugs qui sont communs:

              5---6---7--11--13--15    <-- merged into type 1
             /                   /
1---2---3---4--11--12---+-------+      <-- common fix(es)
             \           \
              8---9--10--14            <-- merged into type 2

Remarque : Cela suppose que vous n'allez pas faire refactoring main lourde dans les deux types ou branches communes, si vous faites cela, vous êtes probablement mieux avec votre situation actuelle, au moins par rapport à un mode branchu comme celui-ci. Toute refactoring ferait l'avenir se confond vraiment douloureux.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top