Frage

Ich habe gehört, wie Leute sagen, dass Codegeneratoren und T4-Vorlagen sollten nicht verwendet werden. Die Logik dahinter ist, dass, wenn Sie Code generieren mit einem Generator dann gibt es einen besseren effizienten Weg, um den Code durch Generika und Templat zu bauen.

Während ich etwas mit dieser Aussage einverstanden ist, habe ich wirklich nicht gefunden wirksame Möglichkeiten, Vorlagen zu erstellen, die zum Beispiel selbst instanziiert sagen können. In anderen Worten kann ich nie tun:

return new T();

Außerdem, wenn ich Code auf Datenbankwerte generieren mag ich, dass zum Kopieren / Einfügen oder verwenden resharper ohne Masse Mengen von Code zu generieren wunderbar gewesen mit T4-Vorlagen Microsoft.SqlServer.Management.SMO in Verbindung gefunden.

Viele der Probleme, die ich mit Generics auch gefunden haben, ist, dass zu meinem Schock viele Entwickler gibt, die sie nicht verstehen. Wenn ich Generika tun untersuchen nach einer Lösung, es gibt Zeiten, in denen es, weil C # Staaten kompliziert wird, dass Sie nicht etwas tun können, die in meinem Kopf logisch erscheinen mag.

Was sind Ihre Gedanken? Bevorzugen Sie einen Generator zu bauen, oder bevorzugen Sie Generika zu benutzen? Auch, wie weit kann Generika gehen? Ich kenne einen angemessenen Betrag über Generika, aber es gibt Fallstricke, die ich immer in die Sache ich laufen zu einer T4-Vorlage zu greifen.

Was ist die richtige Art und Weise mehr Szenarien zu behandeln, in denen Sie eine große Menge an Flexibilität benötigen? Ach ja, und als Bonus auf diese Frage, was sind gute Ressourcen auf C # und Generics?

War es hilfreich?

Lösung

Sie können neue T tun (); wenn Sie dies tun

public class Meh<T>
  where T : new()
{
  public static T CreateOne()
  {
    return new T();
  }
}

Wie bei Code-Generatoren. Ich benutze man jeden Tag ohne Probleme. Ich bin mit einer jetzt in der Tat: -)

Generics lösen ein Problem, Code-Generatoren lösen eine andere. Zum Beispiel ein Geschäftsmodell Erstellung eines UML-Editor und dann Klassen mit Ausdauer-Code zu erzeugen, wie ich die ganze Zeit tun mit dieses Tool nicht mit Generika erzielt werden kann, weil jede persistente Klasse ist völlig anders.

Wie für eine gute Quelle für Generika. Die beste hat bekam Jon Skeet Buch natürlich sein! : -)

Andere Tipps

Als Urheber T4, ich habe diese Frage schon ein paar Mal verteidigen, wie man sich vorstellen kann: -)

Mein Glaube ist am beste Code-Generierung, dass ein Schritt auf dem Weg entspricht Wert unter Verwendung von wieder verwendbaren Bibliotheken zu erzeugen.

Wie viele andere gesagt haben, das Schlüsselkonzept DRY zu halten ist nie, nie generierten Code manuell ändern, sondern die Erhaltung Ihrer Fähigkeit, sich zu regenerieren, wenn die Quellmetadaten ändert oder Sie einen Fehler im Code-Generator finden. An diesem Punkt hat der generierte Code viele der Eigenschaften von Objektcode und Sie nicht in copy / paste-Typ Probleme ausgeführt werden.

Im Allgemeinen ist es viel weniger Aufwand einen parametrisierte Code-Generator (vor allem mit Template-basierten Systemen) zu erzeugen, als es ist richtig eine qualitativ hochwertige Basisbibliothek zu entwickeln, die die Nutzungskosten nach unten auf das gleiche Niveau bekommt, so ist es ein schnelles Art und Weise Wert von Konsistenz zu erhalten und Wiederholungsfehler entfernen.

Aber ich glaube immer noch, dass das fertige System würde am häufigsten durch eine geringere Gesamt Code verbessert werden. Wenn nichts anderes, würde sein Speicherbedarf fast immer deutlich kleiner sein (obwohl Leute von Generika zu denken, neigen dazu, als Kosten frei in diesem Zusammenhang, die sie ganz sicher nicht).

Wenn Sie einen Wert mit einem Code-Generator realisiert haben, dann diese kauft man oft einige Zeit oder Geld oder Goodwill in der Ernte eine Bibliothek aus der generierten Code-Basis zu investieren. Anschließend können Sie den Code-Generator inkrementell Reengineering die neue Bibliothek zielen und hoffentlich viel weniger Code zu erzeugen. Spülen und wiederholen.

Ein interessanter Kontrapunkt, die ich gemacht wurde, und das kommt in diesem Thread nach oben ist, dass reichten, komplexe, parametrische Bibliotheken sind nicht die einfachste Sache in Bezug auf der Lernkurve, vor allem für diejenigen, die nicht tief in der Plattform eingetaucht. Das Festhalten an der Codegenerierung auf einfachere Grundgerüsten kann ausführlichen Code produzieren, aber es kann oft ganz einfach und leicht zu lesen sein.

Natürlich, wo man viel Varianz hat und extrem reich Parametrisierung in Ihrem Generator, können Sie nur aus seinem Handel Komplexität ein, Ihr Produkts für Komplexität in Ihren Vorlagen. Dies ist ein einfacher Weg zu gleiten und so viel Kopfschmerzen nur Wartung machen können -. Achten Sie auf das

Erstellen von Code ist nicht böse und es riecht nicht! Der Schlüssel ist, den richtigen Code zur richtigen Zeit zu erzeugen. Ich denke, T4 ist toll - ich es nur gelegentlich verwenden, aber wenn ich es tun, ist sehr hilfreich. Zu sagen, bedingungslos, dass Code-Generierung schlecht ist bedingungslos verrückt!

Es scheint mir, Code-Generatoren sind in Ordnung, solange die Code-Generierung Teil Ihres normalen Build-Prozesses, anstatt etwas, das man einmal laufen und dann seine Ausgabe halten. Ich füge diesen Vorbehalt, denn wenn nur den Generator einmal Code verwenden und die Daten verwerfen, die sie erstellt hat, sind Sie nur automatisch ein massiver DRY Verletzung und Wartung Kopfschmerzen zu schaffen; während Erzeugung bedeutet, jedes Mal den Code effektiv, dass, was auch immer Sie den Generator zu tun verwendet, ist die eigentliche Quellcode und die erzeugten Dateien sind nur Zwischen Kompilierung Stufen, die Sie meistens ignorieren sollten.

Lex und yacc sind klassische Beispiele für Tools von erlauben Sie Funktionalität in einer effizienten Weise zu spezifizieren und effizienten Code daraus zu generieren. Der Versuch, ihre Arbeit von Hand zu tun, wird die Entwicklungszeit verlängern und wahrscheinlich produziert weniger effizient und weniger lesbaren Code. Und während Sie sicherlich so etwas wie lex und yacc direkt in Ihren Code integrieren können und tun ihre Arbeit zur Laufzeit statt zum Zeitpunkt der Kompilierung, das würde sicherlich einen erheblichen Aufwand, um Ihren Code hinzufügen und verlangsamen. Wenn Sie tatsächlich Ihre Spezifikation zur Laufzeit zu ändern brauchen könnte es sich lohnen, aber in den meisten normalen Fällen lex / yacc unter Verwendung von Code während der Kompilierung für Sie zu erzeugen, ist ein großer Gewinn.

Ein guter Prozentsatz von dem, was in Visual Studio 2010 ist, würde ohne Code-Generierung nicht möglich. Entity Framework nicht möglich wäre. Der einfache Vorgang des Ziehens und eine Steuerung auf eine Form fällt nicht möglich wäre, noch würde Linq. Um die Codegenerierung sagen sollte nicht verwendet wird, ist seltsam, wie so viele es nutzen, ohne auch nur darüber nachzudenken.

Vielleicht ist es ein bisschen hart, aber für mich Codegenerierung riecht.

Das Codegenerierung verwendet, bedeutet, dass es zahlreiche zugrundeliegende gemeinsame Grundsätze, die in einem „Do wiederholen sich nicht“ Art und Weise ausgedrückt werden kann. Es kann etwas länger dauern, aber es ist sehr befriedigend, wenn man mit Klassen am Ende, die nur die Bits enthalten, die wirklich auf einer Infrastruktur auf Basis ändern, die die Mechanik enthält.

Wie zu Generics ... nein, ich habe nicht zu viele Probleme mit ihm. Das einzige, was noch nicht funktioniert sagt, dass

List<Animal> a = new List<Animal>();
List<object> o = a;

Aber auch das wird in der nächsten Version von C # möglich sein.

Weiterer Code bedeutet mehr Komplexität. Mehr Komplexität bedeutet mehr Plätze für Fehler, sich zu verstecken, die länger fix Zyklen bedeutet, was wiederum bedeutet höhere Kosten während des gesamten Projekts.

Wenn immer möglich, ziehe ich die Menge an Code zu minimieren gleichwertige Funktionalität zur Verfügung zu stellen; idealerweise mit dynamischer (programmatischen) nähert sich eher als Codegenerierung. Reflexion, Attribute, Aspekte und Generika viele Optionen für eine DRY-Strategie bieten, Generation als letztes Mittel zu verlassen.

Code-Generierung für mich eine Abhilfe für viele Probleme in der Sprache, Frameworks gefunden ist, usw. Sie selbst nicht böse sind, würde ich sagen, dass es sehr, sehr schlecht ist (dh das Böse) zu lösen, eine Sprache (C #) und Rahmen, zwingt Sie & Paste kopieren (Swap auf Eigenschaften, Ereignisse Auslösung, fehlende Makros) oder magische Zahlen verwenden (wpf Bindung).

Also, ich weinen, aber ich benutze sie, weil ich muss.

Ich habe T4 für die Codegenerierung verwendet und auch Generics. Beide sind gut, haben ihre Vor- und Nachteile, und werden für verschiedene Zwecke geeignet sind.

In meinem Fall verwende ich T4 Entities zu erzeugen, DAL und BLL basierend auf einem Datenbankschema. Allerdings DAL und BLL verweisen auf eine Mini-ORM I gebaut, basierend auf Generics und Reflexion. Also ich glaube, Sie sie nebeneinander verwenden können, solange Sie die Kontrolle behalten und halten es klein und einfach.

T4 erzeugt statischen Code, während Generics dynamisch ist. Wenn Sie Generics verwenden möchten, verwenden Sie Reflexion, die angeblich auf weniger performant als „hart codiert“ Lösung. Natürlich können Sie Reflexion Ergebnisse zwischenzuspeichern.

In Bezug auf "return new T ();", verwende ich dynamische Methoden wie folgt aus:

public class ObjectCreateMethod
    {
    delegate object MethodInvoker();
    MethodInvoker methodHandler = null;

    public ObjectCreateMethod(Type type)
    {
        CreateMethod(type.GetConstructor(Type.EmptyTypes));
    }

    public ObjectCreateMethod(ConstructorInfo target)
    {
        CreateMethod(target);
    }

    void CreateMethod(ConstructorInfo target)
    {
        DynamicMethod dynamic = new DynamicMethod(string.Empty,
                    typeof(object),
                    new Type[0],
                    target.DeclaringType);
        ILGenerator il = dynamic.GetILGenerator();
        il.DeclareLocal(target.DeclaringType);
        il.Emit(OpCodes.Newobj, target);
        il.Emit(OpCodes.Stloc_0);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ret);

        methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
    }

    public object CreateInstance()
    {
        return methodHandler();
    }
}

Dann nenne ich es wie folgt aus:

ObjectCreateMethod _MetodoDinamico = new ObjectCreateMethod(info.PropertyType);
object _nuevaEntidad = _MetodoDinamico.CreateInstance();

Generics und Code-Generierung sind zwei verschiedene Dinge. In einigen Fällen könnten Sie Generika anstelle von Codegenerierung verwenden und für die, ich glaube, Sie sollten. Für die andere Fälle Codegenerierung ist ein mächtiges Werkzeug.

Für alle Fälle, in denen Sie müssen einfach nur Code generieren basierend auf einiger Dateneingabe, Code-Generierung ist der Weg zu gehen. Die naheliegendste, aber keineswegs das einzige Beispiel der Formulareditor in Visual Studio ist. Hier ist der Eingang ist der Designer Daten und die Ausgabe ist der Code. In diesem Fall sind Generika ist wirklich keine Hilfe überhaupt, aber es ist sehr schön, dass VS einfach den Code erzeugt auf dem GUI-Layout basiert.

Code-Generatoren einen Code Geruch könnten in Betracht gezogen, die einen Fehler oder Mangel an Funktionalität im Ziellangauge an.

Zum Beispiel, während es hier gesagt worden ist, dass „Objekte, die bestehen bleiben kann nicht verallgemeinert werden“, wäre es besser, daran zu denken als „Objekte in C #, die automatisch ihre Daten bestehen bleiben können nicht in C # verallgemeinert werden“, weil ich kann sicher in Python durch den Einsatz verschiedener Methoden.

Der Python Ansatz könnte jedoch in statischen Sprachen durch die Verwendung von operator [] (method_name als string) emuliert werden, die entweder einen Funktor oder einen String zurückgibt, je nach Bedarf. Leider ist diese Lösung nicht immer anwendbar, und ein Funktor Rückkehr unbequem sein kann.

Der Punkt, den ich machen will, ist, dass die Code-Generatoren einen Fehler in einer gewählten Sprache angeben, die durch die Bereitstellung einer bequemeren spezialisiert Syntax für das spezifische Problem bei der Hand behandelt werden.

Die copy / paste Art des generierten Codes (wie ORMs machen) kann auch sehr nützlich sein ...

Sie können Ihre Datenbank erstellen und dann die ORM eine Kopie dieser Datenbankdefinition in Ihrer bevorzugten Sprache ausgedrückt erzeugen zu müssen.

Der Vorteil kommt, wenn Sie Ihre ursprüngliche Definition ändern (die Datenbank), drücken Sie kompilieren und die ORM (wenn Sie einen guten haben) können Sie Ihre Kopie der Definition erneut erzeugt. Nun sind alle Verweise auf Ihre Datenbank kann durch den Compiler Typprüfer und Ihren Code überprüft, kompilieren fehlschlagen, wenn Sie Tabellen oder Spalten verwenden, die nicht mehr existieren.

Denken Sie daran: Wenn ich eine Methode ein paar Mal in meinem Code nennen, bin ich spreche nicht auf den Namen, den ich ursprünglich auf dieses Verfahren gegeben hat? Ich halte diesen Namen immer und immer wieder zu wiederholen ... Sprachdesigner dieses Problem erkannt und kamen mit „Typ-Sicherheit“, wie die Lösung auf. Nicht die Kopien zu entfernen (wie DRY schlägt vor, wir tun sollten), aber sie auf ihre Richtigkeit überprüft, statt.

Der ORM generierte Code die gleiche Lösung bringt, wenn zu Tabellen- und Spaltennamen verweisen. Nicht das Entfernen der Kopien / Referenzen, sondern bringen die Datenbankdefinition in Ihrem (typsicher) Sprache, wo Sie Klassen und Eigenschaften stattdessen verweisen. Zusammen mit der Überprüfung Compiler-Typ, löst dies ein ähnliches Problem in ähnlicher Weise: Garantie Kompilierung-Fehler statt Laufzeit diejenigen, wenn Sie veraltete oder falsch geschriebenen Tabellen (Klassen) oder Spalten (Eigenschaften) beziehen sich auf

.

Zitat: Ich habe nicht wirklich wirksame Methoden zur Vorlage gefunden, die zum Beispiel instanziiert selbst sagen können. In anderen Worten kann ich nie tun:

return new T ();

public abstract class MehBase<TSelf, TParam1, TParam2>
    where TSelf : MehBase<TSelf, TParam1, TParam2>, new()
{
    public static TSelf CreateOne()
    {
        return new TSelf();
    }
}

public class Meh<TParam1, TParam2> : MehBase<Meh<TParam1, TParam2>, TParam1, TParam2>
{
    public void Proof()
    {
        Meh<TParam1, TParam2> instanceOfSelf1 = Meh<TParam1, TParam2>.CreateOne();
        Meh<int, string> instanceOfSelf2 = Meh<int, string>.CreateOne();
    }
} 

Code-Generierung, wie Generika, Vorlagen und andere solche Verknüpfungen, ist ein mächtiges Werkzeug. Und wie bei den meisten leistungsfähigen Werkzeugen, es verstärkt die capaility seines Benutzers zum Guten und zum Bösen -. Sie können nicht getrennt werden

Also, wenn Sie Ihren Code Generator gründlich verstehen, alles antizipieren sie produzieren wird, und warum, und wollen es so aus triftigen Gründen zu tun, dann an ihm haben. Aber verwenden Sie es nicht (oder eine der anderen Technik) vorbei an einem Ort, wo Sie nicht sicher sind, wo Sie vorangegangen, oder wie man dorthin kommt.

Einige Leute denken, dass, wenn Sie Ihr aktuelles Problem gelöst bekommen und einige Verhalten implementiert, Sie sind golden. Es ist nicht immer klar, wie viel cruft und Undurchsichtigkeit Sie in Ihrem Weg für den nächsten Entwickler verlassen (was selbst sein könnte.)

Warum kann nicht sein wirklich zum Kopieren / Einfügen, sehr schnell, machen es nicht mehr akzeptabel?

Das ist die einzige Rechtfertigung für die Codegenerierung, die ich sehen kann.

Auch wenn der Generator die Flexibilität, die Sie benötigen, müssen Sie noch lernen, wie man diese Flexibilität nutzen -., Die noch eine weitere Schicht des Lernens und der erforderlichen Prüfungen

Und selbst wenn es in Nullzeit läuft, es bläht noch den Code.

Ich verdrehte die eigene Datenzugriffsklasse. Er weiß alles über Verbindungen, Transaktionen, Stored Procedure parms, etc, etc, und ich hatte nur einmal alle ADO.NET Sachen zu schreiben.

Es ist jetzt so lange her, dass ich (oder sogar sehen) alles mit einem Verbindungsobjekt zu schreiben, hatte in ihm, dass ich würde mich schwer daran zu erinnern, die Syntax offhand.

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