Wenn Sie bereit sind, ein wenig in der IL zu recherchieren, dann können Sie Die meisten des Weges dorthin.
Erstens, vorausgesetzt, Sie haben ConstructorInfo
Instanz, von der Sie wissen, dass sie parameterlos ist, können Sie den Methodenrumpf und die Bytes für den Methodenrumpf wie folgt abrufen (wir werden mit dem Aufbau einer Erweiterungsmethode beginnen, um dies zu tun):
public static bool MightBeCSharpCompilerGenerated(
this ConstructorInfo constructor)
{
// Validate parmaeters.
if (constructor == null) throw new ArgumentNullException("constructor");
// If the method is static, throw an exception.
if (constructor.IsStatic)
throw new ArgumentException("The constructor parameter must be an " +
"instance constructor.", "constructor");
// Get the body.
byte[] body = constructor.GetMethodBody().GetILAsByteArray();
Sie können alle Methodenkörper ablehnen, die keine sieben Bytes haben.
// Feel free to put this in a constant.
if (body.Length != 7) return false;
Der Grund dafür wird im folgenden Code deutlich werden.
In Abschnitt I.8.9.6.6 von ECMA-335 (Common Language Infrastructure (CLI) Partitionen I bis VI) heißt es in CLS-Regel 21:
CLS-Regel 21: Ein Objektkonstruktor muss einen Instanzkonstruktor Konstruktor seiner Basisklasse aufrufen, bevor ein Zugriff auf geerbte Instanzdaten erfolgt. (Dies gilt nicht für Wertetypen, die keine Konstruktoren haben.)
Dies bedeutet, dass vor alles sonst wird der Basiskonstruktor muss genannt werden. Wir können dies in der IL überprüfen. Die IL dafür würde wie folgt aussehen (ich habe die Byte-Werte in Klammern vor den IL-Befehl gesetzt):
// Loads "this" on the stack, as the first argument on an instance
// method is always "this".
(0x02) ldarg.0
// No parameters are loaded, but metadata token will be explained.
(0x28) call <metadata token>
Wir können nun damit beginnen, die Bytes daraufhin zu überprüfen:
// Check the first two bytes, if they are not the loading of
// the first argument and then a call, it's not
// a call to a constructor.
if (body[0] != 0x02 || body[1] != 0x28) return false;
Jetzt kommt das Metadaten-Token. Die call
Anleitung erfordert die Übergabe eines Methodendeskriptors in Form eines Metadaten-Tokens zusammen mit dem Konstruktor. Dies ist ein Vier-Byte-Wert, der durch die MetadataToken
Eigenschaft über die MemberInfo
Klasse (wovon ConstructorInfo
ableitet).
Wir könnte überprüfen, um zu sehen, dass das Metadaten-Token gültig war, aber da wir bereits die Länge des Byte-Arrays für den Methodenrumpf (mit sieben Bytes) überprüft haben und wir wissen, dass nur noch ein Byte übrig ist (die ersten beiden Op-Codes + vier Byte Metadaten-Token = sechs Bytes), müssen wir nicht überprüfen, ob es sich um einen parameterlosen Konstruktor handelt; wenn es Parameter gäbe, gäbe es andere Op-Codes, um die Parameter auf den Stack zu schieben und das Byte-Array zu erweitern.
Schließlich, wenn nichts anderes getan wird im Konstruktor (was bedeutet, dass der Compiler einen Konstruktor erzeugt hat, der nichts anderes tut, als die Basis aufzurufen), ein ret
Anweisung würde nach dem Aufruf des Metadaten-Tokens ausgegeben:
(0x2A) ret
Das können wir folgendermaßen überprüfen:
return body[6] == 0x2a;
}
Es ist zu beachten, warum die Methode genannt wird MightBeCSharpCompilerGenerated
mit Schwerpunkt auf Macht .
Nehmen wir an, Sie haben die folgenden Klassen:
public class Base { }
public class Derived : Base { public Derived() { } }
Beim Kompilieren ohne Optimierungen (typischerweise DEBUG
Modus), fügt der C#-Compiler ein paar nop
Codes (vermutlich zur Unterstützung des Debuggers) für die Derived
Klasse, was zu einem Aufruf von MightBeCSharpCompilerGenerated
false zurückgeben.
Wenn jedoch die Optimierungen eingeschaltet sind (normalerweise, RELEASE
Modus), gibt der C#-Compiler den sieben Byte langen Methodenkörper ohne die nop
Opcodes, so dass es wie folgt aussehen wird Derived
einen vom Compiler erzeugten Konstruktor hat, auch wenn er das nicht tut.
Aus diesem Grund heißt die Methode Might
anstelle von Is
o Has
; sie zeigt an, dass es könnte eine Methode sein, die Sie sich ansehen müssen, aber ich kann es nicht mit Sicherheit sagen. Mit anderen Worten: Sie werden nie ein falsches Negativ erhalten, aber Sie müssen trotzdem nachforschen, wenn Sie ein positives Ergebnis erhalten.