.NET: determina el tipo de "esta" clase en su método estático

94

En un método no estático que podría usar this.GetType()y devolvería el Type. ¿Cómo puedo obtener lo mismo Typeen un método estático? Por supuesto, no puedo simplemente escribir typeof(ThisTypeName)porque ThisTypeNamesolo se conoce en tiempo de ejecución. ¡Gracias!

Yegor
fuente
16
¿Está en un contexto ESTÁTICO y no puede escribir typeof (ThisTypeName)? ¿Cómo?
Bruno Reis
1
No hay nada como 'tiempo de ejecución' dentro de un método estático (asumiendo que no está hablando de un argumento que se pasa a un método estático). En ese caso, simplemente puede decir typeof (RelevantType).
Manish Basantani
2
Un método estático no puede ser virtual. Ya conoces el tipo.
Hans Passant
7
Habrá muchas clases derivadas de una abstracta. La clase abstracta base tiene un diccionario estático <Int, Type>. Así que las clases derivadas se “registran” a sí mismas en constructores estáticos (dic.Add (N, T)). Y sí, conozco el tipo :) Soy un poco vago y no me gusta reemplazar el texto y me preguntaba si "T" se puede determinar en tiempo de ejecución. Disculpe mi mentira, porque era necesario para simplificar la pregunta. Y funcionó;) Ahora hay una solución aceptada. Gracias.
Yegor
Una subclase hereda los métodos estáticos de su superclase, ¿no? ¿No tendría sentido que un método estático de superclase sea útil para todas sus subclases? Estático simplemente significa sin una instancia, seguramente el principio de código común en una clase base común se aplica tanto a los métodos estáticos como a los métodos de instancia.
Matt Connolly

Respuestas:

134

Si está buscando un forro 1 que sea equivalente a this.GetType()los métodos estáticos, intente lo siguiente.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Aunque es probable que esto sea mucho más caro que solo usarlo typeof(TheTypeName).

JaredPar
fuente
1
Este funciona bien. Gracias :) No es tan caro porque se llamará bastante raro.
Yegor
2
Entrase, por "caro" Jared significa que son costosos para el procesador, por lo general significa lento. Pero dijo, "mucho más caro", es decir, más lento. Probablemente no sea lento en absoluto, a menos que esté diseñando un sistema de guía de cohetes.
Dan Rosenstark
1
He visto que GetCurrentMethod causa algunos problemas graves de rendimiento. Pero como está obteniendo el tipo, puede almacenarlo en caché.
Jonathan Allen
51
Esto siempre devuelve la clase que implementa el método actual, no la clase a la que se llamó en el caso de las subclases.
Matt Connolly
3
Supongo que es útil evitar errores si el código alguna vez se migra a diferentes nombres de clase o algo así, pero una buena herramienta de refactorización debería encargarse de typeof(TheTypeName)todos modos.
Nyerguds
59

Hay algo que las otras respuestas no han aclarado del todo y que es relevante para su idea de que el tipo solo está disponible en el momento de la ejecución.

Si usa un tipo derivado para ejecutar un miembro estático, el nombre del tipo real se omite en el binario. Entonces, por ejemplo, compile este código:

UnicodeEncoding.GetEncoding(0);

Ahora use ildasm en él ... verá que la llamada se emite así:

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

El compilador ha resuelto la llamada a Encoding.GetEncoding- no queda rastro de UnicodeEncoding. Eso hace que su idea del "tipo actual" sea absurda, me temo.

Jon Skeet
fuente
24

Otra solución es utilizar un tipo autorreferencial

//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);
    }
}

Luego, en la clase que lo hereda, hago un tipo de autorreferencia:

public class Child: Parent<Child>
{
}

Ahora, el tipo de llamada typeof (TSelfReferenceType) dentro de Parent obtendrá y devolverá el tipo de la persona que llama sin la necesidad de una instancia.

Child.GetType();

-Robar

Rob Leclerc
fuente
He usado esto para patrones singleton, es decir, Singleton <T> ... los miembros estáticos pueden hacer referencia a typeof (T) en mensajes de error o en cualquier otro lugar que sea necesario.
yoyo
1
Hola. Realmente me gusta y aprecio esta respuesta porque me da una solución alternativa para encontrar el tipo secundario de una función base estática.
Bill Software Engineer
1
Buena esa. Sin embargo, es demasiado triste que en C # esto no se pueda hacer sin esta pequeña duplicación de código.
Nombre para mostrar
Hay otro ejemplo de esta solución en stackoverflow.com/a/22532416/448568
Steven de Salas
6

No se puede usar thisen un método estático, por lo que no es posible directamente. Sin embargo, si necesita el tipo de algún objeto, simplemente llámelo GetTypey haga de la thisinstancia un parámetro que debe pasar, por ejemplo:

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

Sin embargo, este parece un diseño deficiente. ¿Estás seguro de que realmente necesitas obtener el tipo de instancia dentro de su propio método estático? Eso parece un poco extraño. ¿Por qué no usar un método de instancia?

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

No entiendo por qué no puede usar typeof (ThisTypeName). Si este es un tipo no genérico, entonces esto debería funcionar:

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

Si es un tipo genérico, entonces:

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

¿Me estoy perdiendo algo obvio aquí?

Tarydon
fuente
7
Esto no funcionará si crea una clase Bar derivada de Foo y luego la clase hereda el Método1, luego una llamada a Bar.Method1 seguirá procesando typeof (Foo), lo cual es incorrecto. El Method1 heredado debería saber de alguna manera que está derivado en Bar, y luego obtener el typeof (Bar).
JustAMartin
0

Cuando su miembro es estático, siempre sabrá de qué tipo forma parte en tiempo de ejecución. En este caso:

class A
{
  public static int GetInt(){}

}
class B : A {}

No puede llamar (editar: aparentemente, puede, vea el comentario a continuación, pero aún estaría llamando a A):

B.GetInt();

debido a que el miembro es estático, no participa en los escenarios de herencia. Ergo, siempre sabes que el tipo es A.

Teun D
fuente
4
Usted puede llamar B.GetInt () - por lo menos, lo que podría si no fuera privada - pero la compilación se traducirá en una llamada a A.GetInt (). ¡Intentalo!
Jon Skeet
Nota sobre el comentario de Jon: la visibilidad predeterminada en C # es privada, por lo tanto, su ejemplo no funciona.
Dan Rosenstark
0

Para mis propósitos, me gusta la idea de @ T-moty. Aunque he usado información de "tipo de autorreferencia" durante años, hacer referencia a la clase base es más difícil de hacer más adelante.

Por ejemplo (usando el ejemplo de @Rob Leclerc de arriba):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Trabajar con este patrón puede ser un desafío, por ejemplo; ¿Cómo devuelve la clase base de una llamada a función?

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

¿O cuando escribe casting?

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

Entonces, trato de evitarlo cuando puedo y lo uso cuando debo. Si es necesario, le sugiero que siga este patrón:

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) {}
}

Ahora puede trabajar (más) fácilmente con BaseClass. Sin embargo, hay momentos, como mi situación actual, en los que no es necesario exponer la clase derivada, desde dentro de la clase base, y usar la sugerencia de @ M-moty podría ser el enfoque correcto.

Sin embargo, usar el código de @ M-moty solo funciona siempre que la clase base no contenga ningún constructor de instancia en la pila de llamadas. Desafortunadamente, mis clases base usan constructores de instancias.

Por lo tanto, aquí está mi método de extensión que tiene en cuenta los constructores de 'instancia' de la clase base:

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
fuente
0

EDITAR Estos métodos funcionarán solo cuando implemente archivos PDB con el ejecutable / biblioteca, como markmnl me señaló.

De lo contrario, será un gran problema para ser detectado: funciona bien en desarrollo, pero tal vez no en producción.


Método de utilidad, simplemente llame al método cuando lo necesite, desde cualquier lugar de su código:

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
fuente
1
StackTrace solo está disponible en compilaciones de depuración
markmnl
Incorrecto: StackTrace estará disponible cuando también implemente archivos .pdb en modo de lanzamiento. stackoverflow.com/questions/2345957/…
T-moty
Tengo tu punto. No es aceptable que un método funcione solo cuando se implementan archivos PDB.
Editaré