C #: Schreiben von MSIL, um eine Präprozessordirektive hinzuzufügen

8

Ist es in C # möglich, MSIL-Code zu schreiben, der dem Code eine Präprozessordirektive hinzufügt, z. B. #warning , wenn eine bestimmte Bedingung erfüllt ist? Oder vielleicht kann dies mit Nachdenken getan werden, ich weiß es nicht.

Ich versuche, ein benutzerdefiniertes Attribut zu schreiben, das bei falscher Anwendung auf die Methode oder Eigenschaft einer Klasse eine Compiler-Warnung generiert. Die Verwendung des vorhandenen Attributs Obsolete funktioniert nicht, da nur die Verwendung meines benutzerdefinierten Attributs die Warnung verursacht, und das möchte ich nicht. Ich möchte, dass der benutzerdefinierte Attributkonstruktor nach einer Bedingung sucht. Wenn diese Bedingung erfüllt ist, wird eine Kompilierungswarnung ausgegeben.

Update: Nachdem ich meine Frage gelesen habe, denke ich, was ich verlange, ist unmöglich, nur weil ich Kompilierzeit- und Laufzeitbeschränkungen mische. Ich denke, ich werde am Ende mit einer Post-Build-Aufgabe gehen, um die gerade erstellte DLL zu überprüfen und Fehlermeldungen ausgeben zu lassen, wenn die Bedingung erfüllt ist.

    
Sarah Vessels 30.01.2010, 19:39
quelle

7 Antworten

6

Ich habe diese Frage von Ihrem vorherigen Thread kommen sehen. Um den großen Jamie Zawinski falsch zu zitieren: "Einige Leute, wenn sie mit einem Problem konfrontiert werden, denken" Ich weiß, ich werde ein Attribut verwenden. "Jetzt haben sie zwei Probleme."

Ein Attribut sind lediglich Out-of-Band-Daten, die in die Metadaten einer Assembly kompiliert werden. Es kann die Programmausführung oder das Werkzeugverhalten nicht beeinflussen, es sei denn, das Programm oder das Werkzeug ist explizit so programmiert, dass es das spezifische Attribut erkennt. Dazu muss Reflection verwendet werden.

Was Sie tun müssen, ist ein eigenes Werkzeug zu schreiben. Es sollte ausgeführt werden, nachdem eine Assembly erstellt wurde und den Post-Build-Schritt für ein Projekt verwendet. Es muss die Assembly laden und Reflection verwenden, um die Typen in der Assembly zu iterieren. Wiederholen Sie für jeden Typ die Methoden mit Type.GetMethods (), und verwenden Sie MethodInfo.GetCustomAttributes (), um ein möglicherweise programmiertes Attribut zu erkennen und zu erstellen.

Sie können mit Type.GetInterfaces () ermitteln, welche Schnittstellen vom Typ implementiert werden. Sie können sich jetzt beschweren, wenn Sie feststellen, dass eine Methode vorhanden ist, die eine Schnittstellenmethode implementiert, aber ein Attribut, das dies besagt, nicht enthält. Und Ihr ultimatives Ziel: Sie können sich beschweren, wenn Sie eine Methode mit einem Attribut sehen, das besagt, dass sie eine Schnittstellenmethode implementiert, aber der Typ diese nicht mehr erbt.

Verwenden Sie Environment.ExitCode, damit das Tool die Erstellung fehlschlägt, wenn Sie etwas anstößig finden. Dies sorgt für die Durchsetzung. Übrigens: Programmierer hassen es wirklich, den Build zu brechen. Das könnte sie ermutigen, das Attribut religiös zu verwenden. Oder es könnte sie ermutigen, den Post-Build-Schritt zu bearbeiten.

    
Hans Passant 30.01.2010, 20:49
quelle
2

Der Compiler speichert zwei Dinge für benutzerdefinierte Attribute:

  • Der Attributkonstruktor, der
  • aufgerufen werden soll
  • Die Daten für jeden Parameter, der an den Konstruktor
  • übergeben werden soll

Der Konstruktor wird nur aufgerufen, wenn die Anwendung ausgeführt wird und jemand GetCustomAttributes für Assembyl, Type, MethodInfo, ParameterInfo usw. aufruft.

Sie müssen noch einige andere Optionen in Betracht ziehen:

  • Schreiben Sie eine benutzerdefinierte MSBuild-Task, die nach der Kompilierungsstufe ausgeführt wird, lädt die kompilierte Assembly und überprüft die Attributverwendung der Anwendung.
  • Verwenden Sie das Attribut AttributeUsage , um die Codeelemente anzugeben, auf die das Attribut angewendet werden kann.
  • Verzögern Sie die Attributüberprüfung auf Laufzeit.
Sam Harwell 30.01.2010 19:45
quelle
2

Kurz gesagt, nein. Vorverarbeitungsdirektiven haben keine IL-Repräsentation, da sie nur als Metadaten existieren, die beim Kompilieren einer Quelldatei verwendet werden.

Die Art von Sache, die Sie tun, könnte besser als eine benutzerdefinierte FxCop-Regel sein.

    
bobbymcr 30.01.2010 19:49
quelle
2

Hast du jemals von Boo gehört? Es gibt interessante Möglichkeiten, wie Sie sich in die Compiler-Pipeline einklinken können. Ein solches Merkmal nennt man syntaktische Attribute , die Attribute sind, die eine Schnittstelle implementieren dass der Compiler aufruft, damit sie an der Codegenerierung teilnehmen können.

%Vor%

Die Attribute in diesem Code generieren öffentliche Getter für die Felder und Nullprüfungen für die Konstruktorparameter. Es wird alles in der kompilierten Assembly enden.

Ich wollte schon immer diese Art der Erweiterbarkeit haben der C # -Compiler. Vielleicht wird es eines Tages. Bis dahin können Sie einen Post-Compiler wie CciSharp verwenden. CCiSharp wird die CIL basierend auf speziellen Attributen in der Assembly neu schreiben, genau wie bei syntaktischen Attributen von Boo.

Gegeben dieser Code:

%Vor%

CCiSharp mutiert den Code basierend auf LazyAttribute zu diesem:

%Vor%

CCiSharp basiert auf dem Projekt Common Compiler Infrastructure , das zur Implementierung von Code-Verträge Post-Compiler im kommenden .NET Framework 4.0.

So können Sie das generierte CIL so mutieren.

Aber eine #warning -Direktive hat keine CIL-Darstellung, sondern nur eine Compiler-Direktive. Um diese Anweisung hinzuzufügen, müssen Sie nicht die generierte CIL, sondern den C # -Code selbst mutieren. Sie müssten dafür einen C # Parser implementieren. Ich denke, die beste Option ist, wie in anderen Antworten angegeben, ein Post-Build-Ereignis zu erstellen, das über die generierte Assembly reflektiert und die gewünschte Warnung ausgibt.

    
Jordão 06.02.2010 23:20
quelle
1

Sie könnten dies wahrscheinlich mit einer Post-Build-Aufgabe tun, die Ihren kompilierten Code widerspiegelt prüft auf den Zustand. Ich habe keine Erfahrung beim Erstellen von MSBuild-Aufgaben, aber es ist ein Ort, den Sie starten können.

Ein weiterer Vorschlag nach der Veröffentlichung von .NET 4.0 ist die Verwendung von Code Contracts um Anforderungen für die Parameter des Konstruktors des Attributs anzugeben. Dies würde zur Kompilierzeit abgefangen werden.

    
Josh 30.01.2010 19:43
quelle
1

Mein Bauchgefühl ist nein, Sie können keine #warning-Direktive basierend auf einem benutzerdefinierten Attribut und einer Bedingung einfügen, da dies beim Kompilieren vom Compiler abgefangen wird, es ist wie ein Huhn und die Ei-Situation, wie das benutzerdefinierte Attribut sein müsste zuerst ausgewertet, bevor eine #Warnung eingefügt wird, aber damit dies geschieht, muss zuerst eine Kompilierungsaktion ausgeführt werden.

Hoffe, das hilft, Freundliche Grüße, Tom.

    
t0mm13b 30.01.2010 20:31
quelle
0

Ich vermute, die Antwort wäre nein, das geht nicht, weil die #warning-Direktive eine CSC-Sache ist (d. h. Sie weisen den Compiler an, auf eine bestimmte Art zu agieren). Beim Schreiben von Raw-MISL kommt CSC offensichtlich nicht in den Mix, daher gibt es keinen Compiler, der etwas tun kann.

Grundsätzlich ist die Anweisung a (wie '#warning' anzeigen eine Anweisung an CSC, sich unter bestimmten Bedingungen in einer bestimmten Weise zu verhalten.

    
Kevin Won 30.01.2010 19:45
quelle