Pregunta

Me pidieron que mantuviera un código no tan heredado como me gustaría, y está plagado de directivas de compilación, lo que lo hace prácticamente ilegible y casi tan fácil de mantener. Caso en cuestión:

#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);
        }

Las directivas using son aún más bonitas:

#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

Pensé en dar el ConditionalAttribute un intento, pero eso no funcionará del todo en esta situación

¿Hay alguna forma de salir de esta pesadilla de la directiva del compilador?

El código se compila contra .NET 3.5.

ACTUALIZACIÓN:
Oded respondió sugiriendo eliminar las directivas del compilador alrededor del método BeforeAdd, sobrecargándolo así. Desafortunadamente, eso no funcionará, ya que se supone que ambos métodos anulan una clase AbstractBusiness que proporciona dos implementaciones diferentes dependiendo de qué ensamblados terminen siendo incluidos:

protected virtual void BeforeAdd(TEntity entity) {}

o

protected virtual void BeforeAdd(AbstractBusinessEntity entity) {}

Este código obtiene sus dependencias de un conjunto de bibliotecas que la empresa creó en el pasado y que ha estado "actualizando" desde entonces. Ahora tienen 4 versiones diferentes de ese conjunto de bibliotecas con espacios de nombres en conflicto e implementaciones diferentes. Todo en nombre de la "compatibilidad con versiones anteriores" con aplicaciones que utilizan versiones (muy) antiguas.


<×CONCLUSION

Terminé eligiendo la respuesta de @ Oded porque tiene más sentido como enfoque general ( KISS y todo eso). Sin embargo, no pude usarlo en este caso; lo que ves aquí es solo la punta del iceberg. No quisiera K.I.S.S. este código si me pagó.

¿Fue útil?

Solución

En el primer caso, parece que simplemente podría tener varias sobrecargas del método en lugar de esta construcción.La resolución de la sobrecarga debería resolver las cosas en este punto.

En el segundo caso (usando directivas), puede usar un alias para algunas de las directivas e incluirlas todas, usando el alias donde sea necesario.¿Qué sucede cuando se incluyen todos los espacios de nombres?¿Alguna colisión de nombres?

Otros consejos

Yo diría que el problema no está en esta clase. Esta clase es solo un síntoma. El problema está en la clase base que llama a BeforeAdd. Si puede refactorizar allí, no necesitará las compilaciones condicionales.

Si tiene nombres y espacios de nombres en conflicto, puede solucionarlo con la palabra clave using (no la de ensamblados).

Para que pueda hacer algo como

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

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

También creo que el problema está en la clase base, no en esta clase, per se.

En ese caso, puede evitar esta tontería utilizando la adaptación o la interfaz.

No sé cómo se llama la otra clase, pero digamos que se llama EntityAggregator.

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

luego en su clase base de agregador:

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

luego en su subclase:

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

Ahora puede adaptar los otros objetos para que sean IEntity implementando esa interfaz.

Cuando miro este código, también me sorprende que quizás estés usando eventos en lugar de este código.

Ahora, si está hablando de compilación de uso múltiple, donde el código se compila en dos lugares separados bajo dos condiciones diferentes, entonces puede hacerlo de manera más elegante usando clases parciales.

Aísla el código CONDITION_1 en algo como esto:

// 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);
    }
}

No me gusta esto en este caso debido a la repetición del código. Puede ayudar con el uso de la palabra clave using:

#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);
}

Según lo que veo, parece que el desarrollador original no tenía ningún sentido de herencia y polimorfismo.Es un poco difícil de distinguir por el código, pero parece que LogEntity y AbstractBusinessEntity comparten propiedades comunes.¿Existe un modelo de herencia o son dos clases completamente no relacionadas?Si no están relacionados, ¿podría crear un modelo de herencia o una interfaz que ambos puedan implementar?Podría ayudar si pega las clases.

En pocas palabras, no perdería mi tiempo tratando de trabajar con ese código en su forma actual.Encontraría una manera de eliminar las directivas del compilador, a toda costa.No parece que sea completamente irrecuperable, pero podría requerir un poco de esfuerzo.

No sé si es práctico, pero lo que haría sería crear ramas en mi DVCS, Mercurial, para manejar esto.

Tendría 2 ramas en juego y una tercera temporalmente mientras soluciono errores / agrego código que es común.

Así es como crearía las versiones iniciales:

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

Para corregir errores en solo uno de ellos:

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

Para corregir errores que son comunes:

              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

Nota : esto supone que no va a realizar una refactorización torpe en las ramas de tipo o comunes, si lo hace, probablemente esté mejor con su situación actual, al menosen comparación con una forma ramificada como esta.Cualquier refactorización de este tipo haría que las fusiones futuras realmente fueran dolorosas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top