2010-06-10 25 views
6

Ich möchte eine C# -Datei analysieren. Das einzige, was ich möchte, ist festzustellen, ob es eine Eigenschaft mit einem bestimmten Namen enthält; nur eine einfache wahre/falsche Antwort. Oder besser gesagt, da ich in jedem Lauf für mehr als eine Eigenschaft hatte die Überprüfung, eine Liste der Eigenschaftsnamen extrahieren könnte hilfreich seinExtrahieren von Eigenschaftsnamen aus einer C# -Quelldatei

Ich dachte, dass ich eine elegante Lösung mit der CodeDomProvider Funktionalität (f # Beispiel) erstellen können:

Leider ist die Parse Funktion nicht für die CSharpCodeProvider implementiert. Gibt es eine Möglichkeit, eine aus einer Quelldatei zu bekommen? Oder gibt es einen anderen eleganten Weg? (Ich hatte gehofft, reguläre Ausdrücke zu vermeiden)?

Edit: Ich werde dies für die automatische Code-Generierung verwenden. Im Grunde werde ich eine partielle Klasse in der Datei xyz.partial.cs erzeugen. Dies erzeugt eine Skeletteigenschaft. Aber wenn ich die Implementierung einer Eigenschaft ändern möchte, werde ich diese Eigenschaft ausschneiden und in die handcodierte xyz.cs einfügen. Wenn ich die generierte Klasse neu erzeuge, möchte ich, dass Eigenschaften, die ich in die handcodierte Datei verschoben habe, übersprungen werden.

Daher kommt Reflexion nicht in Frage, denn die Reflektion sagt mir, dass die Eigenschaft tatsächlich existiert, aber nicht, wenn sie in der einen oder anderen Datei definiert ist.

+0

Trotzdem jucke ich, um Expresso zu öffnen und es zu versuchen. Ich kann nichts ausdrücken und keine Macht in dem Vers kann mich aufhalten. –

+1

Haben Sie über die kompilierte Version der Quelle nachgedacht? Oder ist das keine Option? Nicht sicher, ob Reflektion Eigenschaftsnamen bereitstellt. –

+0

@Moron - Das sollte eigentlich ganz gut funktionieren. – ChaosPandion

Antwort

0

Manchmal RegEx ist die einzige elegante Lösung. Das sollte das sein, wonach Sie suchen. Es wird Ihnen die Namen aller Immobilien in der Codedatei, und nichts mehr:

(?:"(?:(?:(?:\\.)|[^"\\\r\n])*)"|'(?:(?:(?:\\.)|[^'\\\r\n])*)'|@"(?:(?:(?:"")|[^"])*)")|(?:(?://.*)|(?:/\*(?:(?:[^*]|\*(?!/))*)\*/))|(?:[\w?<>]\s+(\w+)\s*\{\s*(?:get|set)\s*[{;]) 

Es wird nicht einen ähnlichen Code in den Kommentaren oder Strings und muss passen nur eine kleine Änderung der Unterkunft Typ zurückzukehren. Der Name wird in capture \ 1 angezeigt, ist jedoch leer, wenn es keine echte Übereinstimmung ist.

+2

Sie nennen es "elegant"? "Manche Leute, wenn sie mit einem Problem konfrontiert sind, denken" Ich weiß, ich werde reguläre Ausdrücke verwenden. "Jetzt haben sie zwei Probleme" –

+0

Was würden Sie verwenden? Verschachtelte Schleifen und Teilstrings? Eine Nummer fehl am Platz und Sie haben eine unvorhersehbare Explosion von Problemen. Regex war für mich nie ein Problem, und oft wird die Arbeit in nur einer Codezeile erledigt. Viel effizienter. – Patrick

+0

Ich erkannte, dass ich wahrscheinlich Regex verwenden muss, und dieser hat mich gestartet, aber es gibt einige Dinge, die nicht übereinstimmen: Eigenschaften mit generischen oder nullable Typen (weil der Name des Typs nicht in einem Zeichen endet, aber a? oder a>), und es passt nicht automatisch Eigenschaften (Eigenschaften mit automatischem Hintergrundfeld) – Pete

1

EDIT 2: Basierend auf den hinzugefügten Informationen würde ich sagen, dass Sie am besten die handcodierte Klasse kompilieren und dann diese Klasse reflektieren. Sie können dann den Code für die partielle Klassendatei generieren.

EDIT: Ich habe etwas mehr Forschung getan und es scheint, dass Sie kein Glück haben. CodeDom kann nicht zum Parsen von Code verwendet werden. http://blogs.msdn.com/b/bclteam/archive/2005/03/16/396929.aspx

Es gibt ein Beispiel zum Erstellen einer CSharpCodeProvider-Instanz unter MSDN.

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 
CodeCompileUnit ccu = provider.Parse(reader); 

Anschließend können Sie navigieren die CodeCompileUnit (weitere Dokumentation auf CodeCompileUnit).

Ich hoffe, es hilft.

+0

Es scheint, dass die Parse-Methode nicht von allen CodeDomProvider-Klassen implementiert wird. Die ursprüngliche Frage klingt so, als ob sie nicht von C# implementiert wird. Wer weiß es sicher? –

+0

Sie sind zur Laufzeit implementiert denke ich. Haben Sie versucht, den Code tatsächlich auszuführen? Wenn es keine Ausnahmen zur Laufzeit gibt, schlage ich vor, dass Sie es versuchen. –

+0

So fing ich an. Es gibt das gleiche Ergebnis NotImplementedException. CodeDomProvider.CreateProvider ("CSharp") gibt einfach einen CSharpCodeProvider zurück. – Pete

0

Ich denke, Sie können dies lösen, indem Sie PropertyInfo zur Laufzeit verwenden. Es verwendet Reflektion, um alle Informationen für einen Typ zurückzugeben.Um alle Eigenschaftsnamen von einem Typ, versuchen Sie dies:

void GetMeTheProperties(object source) 
{ 
    Type sourceType = source.GetType(); 

    foreach (PropertyInfo sourceProperty in sourceType.GetProperties()) 
    { 
     int i = 1; 
     Console.WriteLine("Property {0}: {1}", i, sourceProperty.Name; 
    } 
} 

Sie können auch bestimmen, ob eine bestimmte benannte Eigenschaft in einer Art durch ein ähnliches Verfahren ist:

bool PropertyExists(string propertyName, object source) 
{ 
    Type sourceType = source.GetType(); 
    return (from var s in sourceType.GetProperties() select s).Where(i => i.Name == propertyName).Any(); 
} 
+0

Leider ist Reflexion keine Option, siehe bearbeiten aus einem Grund, warum – Pete

+0

Sorry ... die Frage falsch interpretiert. – Odhran

0

Ist die Datei, die Sie Generieren des Codes von kompiliert? Wenn ja, könnten Sie versuchen, ein Attribut zu erstellen, das zu allen Eigenschaften hinzugefügt werden soll, die nicht kopiert werden sollen. Dann können Sie mit Reflection die Attribute durchlesen und diese überspringen.

internal class DoNotCopyAttribute: Attribute{} 

// then add this to Odhran's GetMeTheProperties 
bool skip=false; 
foreach (System.Attribute attr in System.Attribute.GetCustomAttributes(sourceProperty)) { 
    if (attr is DoNotCopyAttribute){ skip=true; break; } 
} 
if(skip) continue; 
0
var provider = CodeDomProvider.CreateProvider("c#"); 
var parameters = new CompilerParameters 
{ 
    WarningLevel = 3 // for example, tune how you need 
}; 
var result = provider.CompileAssemblyFromSource(parameters, new string[] { "source" }); 

if (!result.Errors.HasErrors) 
{ 
    var assembly = result.CompiledAssembly; 

    bool containsLocalAppDomain = assembly 
     .GetTypes() 
     .SelectMany(t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) 
     .Any(p => p.Name == "YourProperty"); 

    // indeed it's much better not to load compiled assembly in current appDomain, but create a new one 
    var appDomain = AppDomain.CreateDomain("YourNewDomain", null, null); 
    bool containsNewAppDomain = appDomain 
     .GetAssemblies() 
     .SelectMany(a => a 
      .GetTypes() 
      .SelectMany(t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public))) 
     .Any(p => p.Name == "YourProperty"); 

btw, wie wollen Sie so weit Teileigenschaften implementieren, da die nicht unterstützt werden?

Verwandte Themen