Selectoren
Der "Selector" ist der erste Teil einer Regel, die from-Klausel oder
der initiale Collection-Zugriff. Er bestimmt auf welcher Sorte
Code-Element die Regel arbeitet.
Top-Level-Collections
Diese kannst du direkt als Startpunkt nutzen:
| Collection | Element-Typ | Beschreibung |
|---|---|---|
Types |
TypeModel |
Alle Klassen, Structs, Interfaces, Enums |
Methods |
MethodModel |
Alle Methoden inkl. Konstruktoren-Variante |
Properties |
PropertyModel |
Alle Properties |
Fields |
FieldModel |
Alle Felder |
Events |
EventModel |
Alle Events |
Namespaces |
NamespaceModel |
Alle Namespaces |
Assemblies |
AssemblyModel |
Alle analysierten Assemblies |
TypeDependencies |
TypeDependency |
Coupling-Beziehungen zwischen Typen |
SyntaxIssues |
SyntaxIssue |
Vor-analysierte Pattern-Befunde |
Zwei Schreibweisen
Die DSL unterstützt sowohl from x in Y (Query-Syntax) als auch
Y.Where(x => ...) (Method-Syntax). Beide produzieren das gleiche
Ergebnis.
Query-Syntax:
from t in Types
where t.Kind == "Class"
where t.IsAbstract
select t
Method-Syntax:
Types.Where(t => t.Kind == "Class" && t.IsAbstract)
Wann welche? Bei mehreren where-Klauseln ist Query-Syntax lesbarer.
Bei einer Bedingung oder einer Aggregation (.Count > 10) ist
Method-Syntax kürzer.
Per-Element-Properties
Die Element-Typen haben jeweils Properties die du in where-Klauseln
und Sub-Queries nutzen kannst.
TypeModel (Auszug)
t.Name // einfacher Name
t.FullName // mit Namespace
t.Namespace // nur der Namespace
t.Kind // "Class", "Interface", "Struct", "Enum"
t.IsAbstract // bool
t.IsSealed // bool
t.IsStatic // bool
t.LinesOfCode // int
t.NumberOfMethods // int
t.Methods // collection of MethodModel
t.Constructors // collection of MethodModel
t.NestedTypes // collection of TypeModel
t.DerivedTypes // collection of TypeModel
t.UsedTypes // welche Typen referenziert dieser Typ
t.UsedByTypes // welche Typen referenzieren diesen
t.BaseType.FullName // Vater-Klasse
MethodModel (Auszug)
m.Name // Methodenname
m.AccessModifier // "Public", "Private", "Protected", "Internal"
m.IsAsync // bool
m.IsStatic // bool
m.IsOverride // bool
m.IsInterfaceImplementation // bool
m.Parameters // collection of ParameterModel
m.CalledMethods // welche Methoden ruft diese auf
m.CalledByMethods // welche Methoden rufen diese
m.LinesOfCode // int
m.DeclaringType // TypeModel
Die vollständige Schema-Liste ist in der CLI über codeguard list-rules --schema
abrufbar (geplant, kommt mit der nächsten Minor-Version).
Composition mit Any / All / Count
Sub-Queries auf Collection-Properties:
# Klassen mit mindestens einer async Methode ohne CancellationToken
from t in Types
where t.Methods.Any(m =>
m.IsAsync &&
!m.Parameters.Any(p => p.TypeShortName == "CancellationToken"))
select t
# Klassen mit mehr als 10 public Methoden
from t in Types
where t.Methods.Where(m => m.AccessModifier == "Public").Count > 10
select t
SyntaxIssues als Selector
Manche Pattern erkennt der Analyzer syntaktisch (nicht semantisch über
das Typmodell) und stellt sie als SyntaxIssue zur Verfügung. Eine
Regel kann gezielt auf eine Kategorie filtern:
@name "Direct DateTime usage"
@severity warn
@category "Testability"
SyntaxIssues.Where(s => s.Kind == "DateTimeDirectUsage")
Die Kind-Werte sind festgelegt. Liste in der Schema-Doku
(siehe Syntax-Übersicht).
Was als nächstes
- Suppressions, wenn eine Stelle bewusst ausgenommen werden soll
- Best Practices, gute vs. schlechte Selector-Strategien