.NET: Bestimmen Sie den Typ dieser Klasse in ihrer statischen Methode

94

In einer nicht statischen Methode könnte ich verwenden this.GetType()und es würde die zurückgeben Type. Wie kann ich dasselbe Typemit einer statischen Methode erreichen? Natürlich kann ich nicht einfach schreiben, typeof(ThisTypeName)weil ThisTypeNamees nur zur Laufzeit bekannt ist. Vielen Dank!

Jegor
quelle
16
Sie befinden sich in einem STATISCHEN Kontext und können typeof (ThisTypeName) nicht schreiben? Wie?
Bruno Reis
1
Es gibt nichts Besseres als 'Laufzeit' in einer statischen Methode (vorausgesetzt, Sie sprechen nicht von einem Argument, das an eine statische Methode übergeben wird). In diesem Fall können Sie einfach typeof (RelevantType) sagen.
Manish Basantani
2
Eine statische Methode kann nicht virtuell sein. Sie kennen den Typ bereits.
Hans Passant
6
Es wird viele abgeleitete Klassen von einer abstrakten geben. Die abstrakte Basisklasse verfügt über das statische Wörterbuch <Int, Type>. Abgeleitete Klassen „registrieren“ sich also in statischen Konstruktoren (dic.Add (N, T)). Und ja, ich kenne den Typ :) Ich bin nur ein bisschen faul und möchte den Text nicht ersetzen und habe mich gefragt, ob "T" zur Laufzeit bestimmt werden kann. Bitte entschuldigen Sie meine Lüge, denn es war notwendig, um die Frage zu vereinfachen. Und es hat funktioniert;) Es gibt jetzt eine akzeptierte Lösung. Vielen Dank.
Jegor
Eine Unterklasse erbt statische Methoden ihrer Oberklasse, nicht wahr? Wäre es nicht sinnvoll, wenn eine statische Methode der Oberklasse für alle ihre Unterklassen nützlich wäre? Statisch bedeutet einfach ohne Instanz, dass das Prinzip des gemeinsamen Codes in einer gemeinsamen Basisklasse sicherlich sowohl für statische als auch für Instanzmethoden gilt.
Matt Connolly

Antworten:

134

Wenn Sie nach einem 1-Liner suchen, der this.GetType()statischen Methoden entspricht, versuchen Sie Folgendes.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Obwohl dies wahrscheinlich viel teurer ist als nur zu verwenden typeof(TheTypeName).

JaredPar
quelle
1
Dieser funktioniert gut. Danke :) Es ist nicht so teuer, weil es ziemlich selten genannt wird.
Jegor
2
Entrase bedeutet "teuer". Jared bedeutet, dass sie für den Prozessor teuer sind, was normalerweise langsam bedeutet. Aber er sagte, "viel teurer" bedeutet langsamer. Wahrscheinlich überhaupt nicht langsam, es sei denn, Sie entwerfen ein Raketenleitsystem.
Dan Rosenstark
1
Ich habe gesehen, dass GetCurrentMethod einige ernsthafte Leistungsprobleme verursacht. Da Sie jedoch nur den Typ erhalten, können Sie ihn zwischenspeichern.
Jonathan Allen
50
Dies gibt immer die Klasse zurück, die die aktuelle Methode implementiert, nicht die Klasse, die bei Unterklassen aufgerufen wurde.
Matt Connolly
3
Ich denke, es ist praktisch, Fehler zu vermeiden, wenn Code jemals auf andere Klassennamen oder ähnliches migriert wird, aber ein gutes Refactoring-Tool sollte sich typeof(TheTypeName)trotzdem darum kümmern .
Nyerguds
59

Es gibt etwas, das in den anderen Antworten nicht ganz geklärt ist und das für Ihre Vorstellung relevant ist, dass der Typ nur zur Ausführungszeit verfügbar ist.

Wenn Sie einen abgeleiteten Typ verwenden, um ein statisches Element auszuführen, wird der Name des realen Typs in der Binärdatei weggelassen. Kompilieren Sie beispielsweise diesen Code:

UnicodeEncoding.GetEncoding(0);

Verwenden Sie jetzt ildasm darauf ... Sie werden sehen, dass der Anruf wie folgt ausgegeben wird:

IL_0002:  call       class [mscorlib]System.Text.Encoding 
[mscorlib]System.Text.Encoding::GetEncoding(int32)

Der Compiler hat den Aufruf von aufgelöst Encoding.GetEncoding- es ist keine Spur UnicodeEncodingmehr vorhanden. Das macht Ihre Vorstellung von "dem aktuellen Typ" leider unsinnig.

Jon Skeet
quelle
24

Eine andere Lösung besteht darin, einen selbstreferenzierenden Typ zu verwenden

//My base class
//I add a type to my base class use that in the static method to check the type of the caller.
public class Parent<TSelfReferenceType>
{
    public static Type GetType()
    {
        return typeof(TSelfReferenceType);
    }
}

Dann mache ich in der Klasse, die es erbt, einen selbstreferenzierenden Typ:

public class Child: Parent<Child>
{
}

Jetzt ruft der Anruftyp typeof (TSelfReferenceType) in Parent den Typ des Anrufers ab und gibt ihn zurück, ohne dass eine Instanz erforderlich ist.

Child.GetType();

-Rauben

Rob Leclerc
quelle
Ich habe dies für Singleton-Muster verwendet, dh Singleton <T> ... statische Mitglieder können dann in Fehlermeldungen oder wo immer es sonst benötigt wird, auf typeof (T) verweisen.
Yooy
1
Hallo. Ich mag und schätze diese Antwort sehr, weil sie mir die Möglichkeit gibt, den untergeordneten Typ anhand einer statischen Basisfunktion zu finden.
Bill Software Engineer
1
Schön. Zu traurig, dass dies in C # ohne diese kleine Codeduplizierung nicht möglich ist.
Anzeigename
Es gibt ein weiteres Beispiel für diese Problemumgehung
Steven de Salas
6

Sie können nicht thisin einer statischen Methode verwenden, daher ist dies nicht direkt möglich. Wenn Sie jedoch den Typ eines Objekts benötigen, rufen Sie GetTypees einfach auf und machen Sie die thisInstanz zu einem Parameter, den Sie übergeben müssen, z.

public class Car {
  public static void Drive(Car c) {
    Console.WriteLine("Driving a {0}", c.GetType());
  }
}

Dies scheint jedoch ein schlechtes Design zu sein. Sind Sie sicher, dass Sie den Typ der Instanz selbst wirklich in eine eigene statische Methode integrieren müssen? Das scheint ein bisschen bizarr. Warum nicht einfach eine Instanzmethode verwenden?

public class Car {
  public void Drive() { // Remove parameter; doesn't need to be static.
    Console.WriteLine("Driving a {0}", this.GetType());
  }
}
John Feminella
quelle
3

Ich verstehe nicht, warum Sie typeof (ThisTypeName) nicht verwenden können. Wenn dies ein nicht generischer Typ ist, sollte dies funktionieren:

class Foo {
   static void Method1 () {
      Type t = typeof (Foo); // Can just hard code this
   }
}

Wenn es sich um einen generischen Typ handelt, dann:

class Foo<T> {
    static void Method1 () {
       Type t = typeof (Foo<T>);
    }
}

Vermisse ich hier etwas Offensichtliches?

Tarydon
quelle
7
Dies funktioniert nicht, wenn Sie eine von Foo abgeleitete Klassenleiste erstellen und die Klasse dann Methode1 erbt. Bei einem Aufruf von Bar.Method1 wird weiterhin der falsche Typ (Foo) verarbeitet. Die geerbte Methode1 sollte irgendwie wissen, dass sie in Bar abgeleitet ist, und dann den Typ (Bar) erhalten.
JustAMartin
0

Wenn Ihr Mitglied statisch ist, wissen Sie zur Laufzeit immer, zu welchem ​​Typ es gehört. In diesem Fall:

class A
{
  public static int GetInt(){}

}
class B : A {}

Sie können nicht anrufen (bearbeiten: anscheinend können Sie, siehe Kommentar unten, aber Sie würden immer noch in A anrufen):

B.GetInt();

Da das Mitglied statisch ist, spielt es in Vererbungsszenarien keine Rolle. Ergo wissen Sie immer, dass der Typ A ist.

Teun D.
quelle
4
Sie können B.GetInt () aufrufen - zumindest, wenn es nicht privat wäre -, aber die Kompilierung übersetzt es in einen Aufruf von A.GetInt (). Versuch es!
Jon Skeet
Hinweis zu Jons Kommentar: Die Standardsichtbarkeit in C # ist privat, daher funktioniert Ihr Beispiel nicht.
Dan Rosenstark
0

Für meine Zwecke mag ich die Idee von @ T-moty. Obwohl ich seit Jahren Informationen zum Typ "Selbstreferenzierend" verwende, ist es später schwieriger, auf die Basisklasse zu verweisen.

Zum Beispiel (am Beispiel von @Rob Leclerc von oben):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Das Arbeiten mit diesem Muster kann beispielsweise eine Herausforderung sein. Wie gibt man die Basisklasse von einem Funktionsaufruf zurück?

public Parent<???> GetParent() {}

Oder beim Typguss?

var c = (Parent<???>) GetSomeParent();

Also versuche ich es zu vermeiden, wenn ich kann, und benutze es, wenn ich muss. Wenn Sie müssen, würde ich vorschlagen, dass Sie diesem Muster folgen:

class BaseClass
{
    // All non-derived class methods goes here...

    // For example:
    public int Id { get; private set; }
    public string Name { get; private set; }
    public void Run() {}
}

class BaseClass<TSelfReferenceType> : BaseClass
{
    // All derived class methods goes here...

    // For example:
    public TSelfReferenceType Foo() {}
    public void Bar(TSelfRefenceType obj) {}
}

Jetzt können Sie (einfacher) mit dem arbeiten BaseClass. Es gibt jedoch Zeiten wie in meiner aktuellen Situation, in denen es nicht erforderlich ist, die abgeleitete Klasse innerhalb der Basisklasse verfügbar zu machen, und die Verwendung des Vorschlags von @ M-moty möglicherweise der richtige Ansatz ist.

Die Verwendung des Codes von @ M-moty funktioniert jedoch nur, solange die Basisklasse keine Instanzkonstruktoren im Aufrufstapel enthält. Leider verwenden meine Basisklassen Instanzkonstruktoren.

Daher hier meine Erweiterungsmethode, die Instanzkonstruktoren der Basisklasse berücksichtigt:

public static class TypeExtensions
{
    public static Type GetDrivedType(this Type type, int maxSearchDepth = 10)
    {
        if (maxSearchDepth < 0)
            throw new ArgumentOutOfRangeException(nameof(maxSearchDepth), "Must be greater than 0.");

        const int skipFrames = 2;  // Skip the call to self, skip the call to the static Ctor.
        var stack = new StackTrace();
        var maxCount = Math.Min(maxSearchDepth + skipFrames + 1, stack.FrameCount);
        var frame = skipFrames;

        // Skip all the base class 'instance' ctor calls. 
        //
        while (frame < maxCount)
        {
            var method = stack.GetFrame(frame).GetMethod();
            var declaringType = method.DeclaringType;

            if (type.IsAssignableFrom(declaringType))
                return declaringType;

            frame++;
        }

        return null;
    }
}
Kabuo
quelle
0

BEARBEITEN Diese Methode funktioniert nur, wenn Sie PDB-Dateien mit der ausführbaren Datei / Bibliothek bereitstellen , wie mir markmnl gezeigt hat.

Andernfalls wird ein großes Problem festgestellt: Funktioniert gut in der Entwicklung, aber möglicherweise nicht in der Produktion.


Utility-Methode: Rufen Sie die Methode bei Bedarf einfach an jeder Stelle Ihres Codes auf:

public static Type GetType()
{
    var stack = new System.Diagnostics.StackTrace();

    if (stack.FrameCount < 2)
        return null;

    return (stack.GetFrame(1).GetMethod() as System.Reflection.MethodInfo).DeclaringType;
}
T-Moty
quelle
1
StackTrace ist nur in Debug-Builds verfügbar
markmnl
Nicht korrekt: StackTrace ist verfügbar, wenn Sie auch PDF-Dateien im Release-Modus bereitstellen. stackoverflow.com/questions/2345957/…
T-moty
Ich habe dich verstanden. Es ist nicht akzeptabel, dass eine Methode nur funktioniert, wenn PDB-Dateien bereitgestellt werden. Ich werde die Antwort bearbeiten
T-moty