Pergunta

Fui solicitado a manter algum código não tão legado quanto eu gostaria, e ele está repleto de diretivas de compilador, tornando-o praticamente ilegível e quase tão sustentável. Caso em questão:

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

as diretivas using são ainda mais 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

Pensei em fornecer o ConditionalAttribute vá, mas isso não funcionará muito bem nesta situação

Existe alguma maneira de sair desse pesadelo das diretivas do compilador?

O código é compilado em relação ao .NET 3.5.

ATUALIZAÇÃO:
Oded respondeu sugerindo a remoção das diretivas do compilador em torno do método BeforeAdd, sobrecarregando-o. Infelizmente, isso não funcionará, pois ambos os métodos devem substituir uma classe AbstractBusiness que fornece duas implementações diferentes, dependendo de quais assemblies acabam sendo incluídos:

protected virtual void BeforeAdd(TEntity entity) {}

ou

protected virtual void BeforeAdd(AbstractBusinessEntity entity) {}

Este código obtém suas dependências de um conjunto de bibliotecas da empresa criadas há algum tempo e que vêm sendo "atualizadas" desde então. Eles agora têm 4 versões diferentes desse conjunto de bibliotecas com namespaces em conflito e implementações diferentes. Tudo em nome da "compatibilidade com versões anteriores" com aplicativos que usam versões (muito) antigas.


<”CONCLUSÃO

Acabei escolhendo a resposta de @Oded porque faz mais sentido como uma abordagem geral ( KISS e tudo isso). Eu não poderia usá-lo neste caso; o que você vê aqui é apenas a ponta do iceberg. Eu não gostaria de K.I.S.S. este código se me pagou.

Foi útil?

Solução

No primeiro caso, parece que você poderia simplesmente ter várias sobrecargas do método em vez desta construção.A resolução da sobrecarga deve cuidar das coisas neste ponto.

No segundo caso (usando diretivas) - você pode criar um alias para algumas das diretivas e incluir todas elas, usando o alias quando necessário.O que acontece quando todos os namespaces são incluídos?Alguma colisão de nomes?

Outras dicas

Eu diria que o problema não está nesta aula. Essa aula é apenas um sintoma. O problema está na classe base que está chamando BeforeAdd. Se você puder refatorar lá, não precisará das compilações condicionais.

Se você tiver nomes e espaços de nomes conflitantes, pode contornar isso com a palavra-chave using (não aquela para assemblies).

Então você pode fazer algo como

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

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

Também acho que o problema está na classe base, não nesta classe em si.

Nesse caso, você pode contornar esse absurdo usando adaptação ou interface.

Não sei como a outra classe é chamada, mas digamos que ela seja chamada de EntityAggregator.

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

então, em sua classe base do agregador:

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

então em sua subclasse:

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

Agora você pode adaptar os outros objetos para serem IEntity implementando essa interface.

Quando eu olho para este código, também me ocorre que talvez você esteja usando eventos em vez deste código.

Agora, se você está falando sobre compilação de uso múltiplo, em que o código está sendo compilado em dois lugares separados sob duas condições diferentes, você pode fazer isso de forma mais elegante usando classes parciais.

Você isola o código CONDITION_1 em algo assim:

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

Não gosto disso neste caso por causa da repetição do código. Você pode ajudar com o uso da palavra-chave 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);
}

Com base no que estou vendo, parece que o desenvolvedor original não tinha nenhum senso de herança e polimorfismo.É um pouco difícil dizer pelo código, mas parece que LogEntity e AbstractBusinessEntity compartilham propriedades comuns.Existe um modelo de herança ou são duas classes completamente não relacionadas?Se eles não estiverem relacionados, você poderia criar um modelo de herança ou uma interface que ambos possam implementar?Pode ajudar se você colar as classes.

Resumindo, eu não perderia meu tempo tentando trabalhar com aquele código em sua forma atual.Eu encontraria uma maneira de eliminar as diretivas do compilador, a todo custo.Não parece ser completamente impossível de ser recuperado, mas pode exigir algum esforço.

Não sei se é prático, mas o que eu faria seria criar branches no meu DVCS, Mercurial, para lidar com isso.

Eu teria 2 branches em jogo e um terceiro temporariamente enquanto corrijo bugs / adiciono código comum.

Aqui está como eu criaria as versões iniciais:

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

Para corrigir bugs em apenas um deles:

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

Para corrigir bugs comuns:

              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 : Isso pressupõe que você não fará refatoração pesada em qualquer tipo ou branches comuns, se fizer isso, provavelmente será melhor com sua situação atual, pelo menosem comparação com uma forma ramificada como esta.Qualquer refatoração desse tipo tornaria as mesclagens futuras realmente dolorosas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top