Frage

Ich wurde gebeten, einen Code beizubehalten, der nicht so alt ist, wie ich es gerne hätte, und er ist voller Compiler-Direktiven, was ihn ziemlich unlesbar und fast genauso wartbar macht. Ein typisches Beispiel:

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

using-Direktiven sind noch hübscher:

#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

Ich dachte, ich würde den ConditionalAttribute angeben a go, aber das wird in dieser Situation nicht ganz funktionieren

Gibt es eine Möglichkeit, mich aus diesem Albtraum der Compiler-Direktive herauszuarbeiten?

Der Code wird gegen .NET 3.5 kompiliert.

UPDATE:
Oded antwortete und schlug vor, die Compiler-Direktiven um die BeforeAdd-Methode zu entfernen und sie so zu überladen. Leider funktioniert das nicht, da beide Methoden eine AbstractBusiness-Klasse überschreiben sollen, die zwei verschiedene Implementierungen bereitstellt, je nachdem, welche Assemblys letztendlich enthalten sind:

protected virtual void BeforeAdd(TEntity entity) {}

oder

protected virtual void BeforeAdd(AbstractBusinessEntity entity) {}

Dieser Code bezieht seine Abhängigkeiten aus einer Reihe von Firmenbibliotheken, die vor einiger Zeit erstellt wurden und seitdem "aktualisiert" wurden. Sie haben jetzt 4 verschiedene Versionen dieses Satzes von Bibliotheken mit kollidierenden Namespaces und unterschiedlichen Implementierungen. Alles im Namen der "Abwärtskompatibilität" mit Anwendungen, die die (sehr) alten Versionen verwenden.


SCHLUSSFOLGERUNG

Am Ende habe ich die Antwort von @ Oded gewählt, weil sie als allgemeiner Ansatz am sinnvollsten ist ( KISS und das alles). Ich konnte es in diesem Fall jedoch nicht verwenden; Was Sie hier sehen, ist nur die Spitze des Eisbergs. Ich würde nicht K.I.S.S. diesen Code, wenn er mich bezahlt hat.

War es hilfreich?

Lösung

Im ersten Fall sieht es so aus, als könnten Sie anstelle dieses Konstrukts einfach mehrere Überladungen der Methode haben.Die Überlastungsauflösung sollte sich an dieser Stelle um die Dinge kümmern.

Im zweiten Fall (mithilfe von Direktiven) können Sie einige der Direktiven aliasisieren und alle einschließen, wobei Sie bei Bedarf den Alias verwenden.Was passiert, wenn alle Namespaces enthalten sind?Irgendwelche Namenskollisionen?

Andere Tipps

Ich würde behaupten, dass das Problem nicht in dieser Klasse liegt. Diese Klasse ist nur ein Symptom. Das Problem liegt in der Basisklasse, die BeforeAdd aufruft. Wenn Sie dort umgestalten können, benötigen Sie die bedingten Kompilierungen nicht.

Wenn Sie widersprüchliche Namen und Namespaces haben, können Sie dies mit dem Schlüsselwort using (nicht das für Assemblys) umgehen.

Sie können also so etwas wie tun

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

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

Ich denke auch, dass das Problem in der Basisklasse liegt, nicht in dieser Klasse an sich.

In diesem Fall können Sie diesen Unsinn umgehen, indem Sie entweder Anpassung oder Schnittstelle verwenden.

Ich weiß nicht, wie die andere Klasse heißt, aber sagen wir, sie heißt EntityAggregator.

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

dann in Ihrer Aggregator-Basisklasse:

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

dann in Ihrer Unterklasse:

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

Jetzt können Sie die anderen Objekte an IEntity anpassen, indem Sie diese Schnittstelle implementieren.

Wenn ich mir diesen Code ansehe, fällt mir auch auf, dass Sie möglicherweise Ereignisse anstelle dieses Codes verwenden.

Wenn Sie nun über die Mehrfachverwendungskompilierung sprechen, bei der der Code an zwei verschiedenen Stellen unter zwei verschiedenen Bedingungen kompiliert wird, können Sie dies mit Teilklassen eleganter tun.

Sie isolieren den Code CONDITION_1 wie folgt:

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

Ich mag dies in diesem Fall nicht, weil sich der Code wiederholt. Sie können dies mit dem Schlüsselwort using unterstützen:

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

Basierend auf dem, was ich sehe, scheint der ursprüngliche Entwickler kein Gefühl für Vererbung und Polymorphismus zu haben.Es ist ein wenig schwierig, es anhand des Codes zu erkennen, aber es scheint, dass LogEntity und AbstractBusinessEntity gemeinsame Eigenschaften haben.Gibt es ein Vererbungsmodell oder handelt es sich um zwei völlig unabhängige Klassen?Wenn sie nicht miteinander verbunden sind, können Sie ein Vererbungsmodell oder eine Schnittstelle erstellen, die beide implementieren können?Es kann hilfreich sein, wenn Sie die Klassen eingefügt haben.

Kurz gesagt, ich würde meine Zeit nicht damit verschwenden, mit diesem Code in seiner aktuellen Form zu arbeiten.Ich würde einen Weg finden, die Compiler-Direktiven um jeden Preis zu entfernen.Es scheint nicht völlig unrettbar zu sein, aber es könnte einige Anstrengungen erfordern.

Ich weiß nicht, ob es praktisch ist, aber ich würde Zweige in meinem DVCS, Mercurial, erstellen, um dies zu handhaben.

Ich hätte 2 Zweige im Spiel und einen dritten vorübergehend, während ich Fehler behebe / Code hinzufüge, der häufig vorkommt.

So würde ich die ersten Versionen erstellen:

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

So beheben Sie Fehler in nur einem von ihnen:

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

So beheben Sie häufig auftretende Fehler:

              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

Hinweis : Dies setzt voraus, dass Sie weder in Typ- noch in allgemeinen Zweigen ein umständliches Refactoring durchführen. Wenn Sie dies tun, sind Sie wahrscheinlich zumindest in Ihrer aktuellen Situation besser dranim Vergleich zu einem verzweigten Weg wie diesem.Ein solches Refactoring würde zukünftige Zusammenschlüsse wirklich schmerzhaft machen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top