2010-01-30 7 views
8

hinzufügen Ist es möglich, in C# MSIL-Code zu schreiben, die eine Präprozessordirektive zum Code hinzugefügt werden, beispielsweise #warning, wenn eine bestimmte Bedingung erfüllt ist? Oder vielleicht kann dies mit Nachdenken getan werden, ich weiß es nicht.C#: Schreiben MSIL eine Präprozessordirektive

Ich versuche, eine Gewohnheit zu schreiben Attribut, das, wenn sie falsch zu einer Klasse Methode oder Eigenschaft angewandt wird, wird eine Compiler-Warnung erzeugen. Die Verwendung des vorhandenen Attributs Obsolete funktioniert nicht, da dann 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 zutrifft, wird eine Kompilierungswarnung ausgegeben.

Update: nach dem Zurücklesen über meine Frage, ich denke, was ich verlange, ist unmöglich, nur weil ich kompilieren Zeit und Laufzeit Einschränkungen mischen. 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 zutrifft.

+1

Ich denke, was Sie sagen, Sie möchten ein benutzerdefiniertes Attribut auf eine Methode angewendet werden und eine # Warning-Direktive ausgeben, wenn eine Bedingung erfüllt ist .. Ich glaube nicht, dass Sie dies tun können, da eine # warning eine Kompilierung ist Zeit Direktive .. – t0mm13b

+0

Gute Frage übrigens ... +1 von mir ... – t0mm13b

Antwort

6

Ich habe diese Frage von Ihrem vorherigen Thread gesehen. 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 Ihr 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 ermitteln und zu erstellen.

Mit Type.GetInterfaces() können Sie 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, um das Tool den Build fehlzuschlagen, wenn Sie etwas anstößig sehen. 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.

+0

Ausgezeichnete Antwort, und die Route, die ich am Ende folgen werde. Ich weiß nicht, ob ich Sie als die ausgewählte Antwort auf diese Frage wählen sollte, weil sie nicht direkt meine MSIL-Frage beantwortet, aber Sie sprechen das Problem hinter einigen meiner letzten Fragen an! –

+2

Willst du sagen, er sollte stattdessen reguläre Ausdrücke verwenden? ;) –

+0

@Remus - klingt wie du das Zitat kennst. Sarah ist wahrscheinlich eine/er. Nein, Regex wird hier nicht angezeigt. –

1

könnten Sie wahrscheinlich dies mit einem post-build task die kompilierten Code und prüft, ob der Zustand widerspiegelt. Ich habe keine Erfahrung beim Erstellen von MSBuild-Aufgaben, aber es ist ein Ort, den Sie starten können.

Ein weiterer Vorschlag einmal .NET 4.0 freigegeben wird, ist Code Contracts zu verwenden Anforderungen an die Parameter des Konstrukteurs-Attribut angeben. Dies würde zur Kompilierzeit abgefangen werden.

2

Der Compiler speichert zwei Dinge für benutzerdefinierte Attribute:

  • Der Attributkonstruktor
  • Die Daten rufen für jeden Parameter an den Konstruktor übergeben

Der Konstruktor nur, wenn die aufgerufen wird, Anwendung läuft und jemand ruft GetCustomAttributes für Ihre Assembyl, Typ, Method, Parameterinfo usw.

Sie haben s Weitere Optionen:

  • Schreiben Sie eine benutzerdefinierte MSBuild-Task, die nach der Kompilierungsstufe ausgeführt wird, die kompilierte Assembly lädt und die Attributverwendung der Anwendung überprüft.
  • Verwenden Sie das AttributeUsage Attribut der Code Elemente angeben, an die das Attribut angewendet werden kann.
  • Verzögern Sie die Attributüberprüfung auf Laufzeit.
  • 0

    Ich vermute, die Antwort wäre nein, Sie können nicht, weil die #warning-Direktive eine CSC-Sache ist (d. H. Sie richten den Compiler auf eine bestimmte Art und Weise). Beim Schreiben von Raw MISL kommt CSC offensichtlich nicht in den Mix, daher gibt es keinen Compiler, der etwas ansteuern könnte.

    Grundsätzlich ist die eine Richtlinie (wie ‚#WARNING‘ anzuzeigen ist eine Anweisung an CSC in gewisser Weise

    2

    Kurz unter bestimmten Bedingungen. Zu verhalten, Nr. Vorbearbeitung Richtlinien haben keine IL Darstellung, da sie nur als Metadaten vorhanden, die beim Kompilieren einer Quelldatei verwendet wurden

    Die Art der Sache, die Sie tun, könnte besser sein als custom FxCop rule.

    1

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

    Hoffen, dass dies hilft, Mit freundlichen Grüßen, Tom.

    2

    Haben Sie jemals von Boo gehört? Es gibt interessante Möglichkeiten, wie Sie sich in die Compiler-Pipeline einklinken können. Ein solches Merkmal heißt syntactic attributes, Attribute, die eine vom Compiler aufgerufene Schnittstelle implementieren, damit sie an der Codegenerierung teilnehmen können.

    class Person: 
        [getter(FirstName)] 
        _fname as string 
    
        [getter(LastName)] 
        _lname as string 
    
        def constructor([required] fname, [required] lname): 
        _fname = fname 
        _lname = lname 
    

    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.

    I've always wanted Diese Erweiterbarkeit ist Teil des C# -Compilers. 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 Boo-Attributen.

    Vor diesem Code:

    class Foo { 
        [Lazy] 
        public int Value { 
        get { return Environment.Ticks; } 
        } 
    } 
    

    CCiSharp den Code mutieren basierend auf dem LazyAttribute dazu:

    class Foo { 
        int Value$Value; // compiler generated 
        int Value$Initialized; 
        int GetValueUncached() { 
        return Environment.Ticks; 
        } 
        public int Value { 
        get { 
         if(!this.Value$Initialized) { 
         this.Value$Value = this.GetValueUncached(); 
         this.Value$Initialized = true; 
         } 
         return this.Value$Value; 
        } 
    } 
    

    CCiSharp basiert auf dem Common Compiler Infrastructure Projekt, das gleiche verwendet, um die code contracts Beitrag zur Umsetzung Compiler im kommenden .NET Framework 4.0.

    So können Sie die generierte CIL so mutieren.

    Aber eine #warning Direktive hat keine CIL-Darstellung, es ist 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.