Autoren: Carsten Schorn und Pascal Brause
Nachdem Entwickler ihr Projekt geschrieben haben, wünschen sich viele eine Bewertung oder Verbesserungsvorschläge ihres Codes herbei. Dafür gibt es bereits zahlreiche, automatisch generierte Analyestools und Metriken. Visual Studio bietet auch eine solche Form von Codeanalyse an, welche sehr umfangreich ist. Die Hinweise sind hier zahlreich: Sie reichen von der Sicherheit über Namenkonventionen und Design bis hin zur Performance. Doch wie kann ich das selbst bestimmen?
Vorhandene Regeln und Regelsätze
Unter den Projekteigenschaften findet sich der Tab „Codeanalyse“. Dort können wir einen von zahlreichen, von Microsoft zu Verfügung gestellten Regelsätzen, auswählen. Ein Regelsatz ist dabei eine Zusammenstellung von Regeln. Wird nun einer der Regelsätze geöffnet, offenbart sich bereits das gesamte Sortiment von vorhandener Regeln.
Bild 1: Vorhandene Regeln von VS 2010
Nun können wir die uns zusagenden Regeln durch Selektieren zusammenstellen. Möchten wir nun diese Regeln auf unser Projekt anwenden, können wir einfach durch einen Rechtsklick im Projektmappenexplorer auf unser Projekt „Codeanalyse“ auswählen. Schon prüft VS 2010 unser Projekt auf alle von uns ausgewählten Regeln des gegebenen Regelsatzes.
Eigene Regelsätze erstellen
Haben wir unsere Lieblingsregeln gefunden und wollen diese dauerhaft abspeichern, ist dies relativ schnell erledigt. Hierzu die gewünschten Regeln auswählen und anschließend unter „Datei“ „speichern unter“ auswählen. Am besten wird diese Datei (*.ruleset) unter dem angebotenen Pfad gespeichert (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\Rule Sets bei 64-bit Systemen und C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\Rule Sets bei 32-bit Systemen). Wenn wir nun den Namen und die Beschreibung noch ein wenig aufhübschen wollen, können wir die eben abgespeicherte Datei mit einem Texteditor öffnen, editieren und die ersten beiden Zeilen mit folgendem Codefragment ersetzen.
<?xml version="1.0" encoding="utf-8"?> <RuleSet Name="Regelname" Description="Beschreibung der Regel." ToolsVersion="10.0">
Nun wird auch in VS 2010 der korrekte Name mit passender Beschreibung angezeigt.
Eigene Regel schreiben
Doch was ist, wenn uns die von Microsoft gegebenen Regeln nicht ausreichen, sondern wir unsere eigene, spezielle Regel aufstellen wollen? Hier wird es ein wenig kniffliger, da Microsoft kaum bis gar keine Dokumentation dazu ausgibt. Doch erstmal eine kurze Erklärung, wie das ganze Konstrukt „eigene Regel“ am einfachsten und effizientesten funktioniert: Wir erstellen ein neues Projekt, schreiben dort die Implementierung der Regel. In eine dazugehörige XML-Datei werden Metadaten hinterlegt, welche mit dem Projekt verknüpft sind. Anschließend bekommen wir eine *.dll-Datei erzeugt, welche unsere Regel darstellt. Zuletzt binden wir diese Regel noch in unseren Regelsatz ein und fertig! Aber alles noch mal Schritt für Schritt:
- Zuerst müssen wir eine neue Projektmappe erstellen und ein dazugehöriges Klassenbibliotheks-Projekt (welches uns die *.dll-Datei liefert) hinzufügen. Dieses haben wir in unserem Beispiel „MyCustomFxCopRules“ genannt (gleiche Namen von Projektmappe und Projekt sind hier wichtig!).
- Nun müssen wir dem Projekt zwei Referenzen einverleiben, ohne welche unsere Regeln nicht funktionieren würden. Diese sind die FxCopSdk.dll und die Microsoft.Cci.dll. Dazu entladen wir unser Projekt (Rechtklick auf das Projekt, „Projekt entladen“) und öffnen die *.csproj- Datei (Rechtsklick auf das entladene Projekt).
- Wir fügen dann folgenden Code in die <ItemGroup> ein:
<Reference Include="FxCopSdk"> <HintPath>$(CodeAnalysisPath)\FxCopSdk.dll</HintPath> <Private>False</Private> </Reference> <Reference Include="Microsoft.Cci"> <HintPath>$(CodeAnalysisPath)\Microsoft.Cci.dll</HintPath> <Private>False</Private> </Reference>
- Nachdem wir gespeichert haben, laden wir das Projekt wieder (Rechtsklick auf das Projekt, “Projekt erneut laden“).
- Jetzt müssen wir die erwähnte XML-Datei anlegen und mit Inhalt füllen. Dazu im Projektmappenexplorer eine XML-Datei hinzufügen (Rechtklick auf das Projekt, „Hinzufügen“, „Neues Element“, „XML-Datei“ auswählen). Diese haben wir hier „RuleMetadata.xml“ genannt.
- In diese XML-Datei stecken wir den unten stehenden Code. Resolution entspricht hier dem Text, der im Fehlerliste-Fenster von VS 2010 angezeigt wird. MessageLevel besagt, was ausgeworfen werden soll, wenn die Regel eintritt. Ebenso FixCategories.
<?xml version="1.0" encoding="utf-8" ?> <Rules FriendlyName="My Custom FxCop Rules"> <Rule TypeName="FalscheNotation" Category="MyRules" CheckId="CR1000"> <Name>FalscheNotation</Name> <Description>Prüft, ob alle Felder einer bestimmten Schreibweise entprechen.</Description> <Resolution>Feld {0} ist nicht in gewuenschter Notation. Feld Name sollte beginnen mit: '{1}'.</Resolution> <MessageLevel Certainty="100">Warning</MessageLevel> <FixCategories>NonBreaking</FixCategories> <Url /> <Owner /> <Email /> </Rule> </Rules>
Extrem wichtig ist, dass der TypeName genau der Klasse entspricht, wie wir sie in der Implementation benutzen. Das sehen wir jetzt, denn nun widmen wir uns der Implementierung: Diese eigene geschriebene Regel prüft lediglich, ob der Name eines static-Felds eine bestimmte Schreibweise hat bzw. ob der Name eines nicht-static-Felds eine bestimmte Schreibweise hat. In diesem konkreten Fall wird geprüft ob der Name eines statischen Feldes mit „static_“ beginnt. Sollte dies nicht der Fall sein, wird eine Warnung ausgegeben. Zweck des ganzen sollte vielmehr die Einbindung der Regel in Visual Studio und die Funktionsweise des ganzes sein.
- In eine *.cs-Datei (Name in unserem Beispiel :„Definition.cs“, aber prinzipiell egal) fügen wir nun folgenden Code ein:
//Folgende Usings benötigt unser Code: using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using Microsoft.FxCop.Sdk; //Muss immer im selben Namespace des Projektes geschrieben werden namespace MyCustomFxCopRules { // Die Standart-Klasse erbt von BaseIntrospectionRule, einer von FxCop vorgegeben Klasse internal abstract class BaseFxCopRule : BaseIntrospectionRule { //Deklaration der Rule (BaseFxCopRule), welche als Namen ("MyCustomFxCopRules.RuleMetadata") unbedingt den Projektnamen + Namen der XML Datei beinhalten muss protected BaseFxCopRule(string ruleName) : base(ruleName, "MyCustomFxCopRules.RuleMetadata", typeof(BaseFxCopRule).Assembly) { } } //Anschließend Kommt die "eigentliche" Klasse bzw. Rule, welche von der Standard-Klasse wiederrum erbt. Exakte Namenskonvention ist einzuhalten! internal sealed class FalscheNotation : BaseFxCopRule { public FalscheNotation() : base("FalscheNotation") { } //Hier müssen wir überschreiben, dass nicht-externe Code Elemente sichtbar gemacht werden, da es diese sind, die analysiert werden. public override TargetVisibilities TargetVisibility { get { return TargetVisibilities.NotExternallyVisible; } } //Überschreiben der Methode Check (von FxCop Basismethode zum Überprüfung allermöglichen Dinge). Hier geschieht die eigentliche Logik der Rule. //Der Returnwert ist vom Typ ProblemCollection, welcher signalisiert, ob ein Warning/Break/Error o.ä. ausgeworfen werden soll //Default wert ist null, was einem "alles in Ordnung" entspricht. public override ProblemCollection Check(Member member) { Field field = member as Field; if (field == null) { //Die Rule erscheint nun nur bei Field //Sollte nichts gefunden worden sein, wird ein null als ProblemCollection zurückgegeben, was ein "alles in Ordnung" repräsentiert. return null; } //Unterscheidung, ob das Feld, was gefunden wurde, statisch ist oder nicht.... if (field.IsStatic) { //Nun wird geschaut ob ein "static_" vor dem Feld steht CheckFieldName(field, s_staticFieldPrefix); } else { //Nun wird geschaut ob ein "nonStatic_" vor dem Feld steht CheckFieldName(field, s_nonStaticFieldPrefix); } return Problems; } private const string s_staticFieldPrefix = "static_"; private const string s_nonStaticFieldPrefix = "nonStatic_"; //Wenn CheckFieldName ein Problem findet (also der string nicht dem entspricht, was in expectedPrefix steht), wird ein "Problem" von Typ ProblemCollection erzeugt. private void CheckFieldName(Field field, string expectedPrefix) { if (!field.Name.Name.StartsWith(expectedPrefix, StringComparison.Ordinal)) { Resolution resolution = GetResolution(field, // Field {0} entspricht nicht der gewünschten Notation. expectedPrefix // Field Name sollte eigentlich beginnen mit: {1}. ); Problem problem = new Problem(resolution); Problems.Add(problem); } } } }
- Wenn wir nun das Projekt erstellen, finden wir unsere gewünschte *.dll-Datei (im Verzeichnis: MyCustomFxCopRules\MyCustomFxCopRules) und kopieren diese in das VS 2010-Regel- Verzeichnis (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\Rules bei 64-bit Systemen und C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\Rules bei 32-bit Systemen).
- Nach einem Neustart von VS 2010 öffnen wir nun unser eigentliches Projekt, auf welches unsere Regel angewendet werden soll. Hier öffnen wir wieder unser vorhin erstellten Regelsatz (unser Beispiel: „MyCustomRuleSet“).
- Nun sollte die erstellte Regel ganz unten unter „MyRules“ angezeigt werden. Wichtig ist, dass nicht aktivierte Regeln angezeigt werden (siehe Bild).Bild 2: Eigene Regel
- Selektieren wir unsere hinzugefügte Regel und speichern den Regelsatz, wird unsere Regel bei der Codeanalyse angewendet.
Quellen: