2010-01-17 5 views
73

En un método no estático podría usar this.GetType() y devolvería el Type. ¿Cómo puedo obtener el mismo Type en un método estático? Por supuesto, no puedo simplemente escribir typeof(ThisTypeName) porque ThisTypeName es conocido solo en tiempo de ejecución. ¡Gracias!.NET: determine el tipo de "esta" clase en su método estático

+15

Estás en un contexto estático y no se puede escribir typeof (ThisTypeName)? ¿Cómo? –

+1

No hay nada como 'tiempo de ejecución' dentro de un método estático (suponiendo que no esté hablando de un argumento que se pasa a un método estático). En ese caso, simplemente puede decir typeof (RelevantType). –

+2

Un método estático no puede ser virtual. Ya sabes el tipo. –

Respuesta

104

Si usted está buscando un revestimiento 1 que es equivalente a this.GetType() para los métodos estáticos, intente lo siguiente.

Type t = MethodBase.GetCurrentMethod().DeclaringType 

aunque esto es probablemente mucho más caro que simplemente usar typeof(TheTypeName).

+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 que "mucho más caro" significa más lento. Probablemente no sea lento en absoluto, a menos que esté diseñando un sistema de guía de cohetes. –

+1

He visto GetCurrentMethod causar algunos problemas graves de rendimiento. Pero dado que solo está obteniendo el tipo, puede almacenarlo en caché. –

6

No puede usar this en un método estático, por lo que no es posible directamente. Sin embargo, si usted necesita el tipo de un objeto, simplemente llame GetType en él y hacer que la instancia this un parámetro que tiene que pasar, por ejemplo .:

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

Este parece ser un mal diseño, sin embargo. ¿Estás seguro de que realmente necesitas obtener el tipo de instancia propia dentro de su propio método estático? Eso parece un poco extraño. ¿Por qué no usar simplemente 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()); 
    } 
} 
0

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

class A 
{ 
    public static int GetInt(){} 

} 
class B : A {} 

no se puede llamar (edit: al parecer, puede, véase el comentario a continuación, pero todavía estaría poniendo en A):

B.GetInt(); 

porque el miembro es estático, no juega parte en escenarios de herencia. Ergo, siempre sabes que el tipo es A.

+4

* Puede * llamar a B.GetInt(); al menos, podría hacerlo si no fuera privado, pero la compilación lo traducirá en una llamada a A.GetInt(). ¡Intentalo! –

+0

Nota sobre el comentario de Jon: la visibilidad predeterminada en C# es privada, por lo tanto, su ejemplo no funciona. –

3

No entiendo por qué no puedes usar typeof (ThisTypeName). Si se trata de 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 se trata de un tipo genérico, entonces:

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

Me estoy perdiendo algo obvio aquí?

+7

Esto no funcionará si crea una clase Bar derivada de Foo y luego la clase hereda el Método1; a continuación, una llamada a Bar.Method1 aún procesará typeof (Foo) que es incorrecto. El Método 1 heredado debería saber de alguna manera que se deriva en Bar, y luego obtener el tipo de (Bar). – JustAMartin

53

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 tiempo de ejecución.

Si utiliza un tipo derivado para ejecutar un miembro estático, real nombre de tipo se omite en el binario. Así, por ejemplo, compilar este código:

UnicodeEncoding.GetEncoding(0); 

Ahora usa ildasm en él ... verá que la llamada se emite la siguiente manera:

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 tu idea del "tipo actual" no tenga sentido, me temo.

24

Otra solución es utilizar un tipo selfreferecing

//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 hereda, hago un tipo de auto referencia:

public class Child: Parent<Child> 
{ 
} 

Ahora el tipo de llamada typeof (TSelfReferenceType) en el interior El padre recibirá y devolverá el tipo de llamante sin necesidad de una instancia.

Child.GetType(); 

-Rob

+1

¡Eso funciona para mí! Gracias Rob. :) –

+0

Lo he usado para patrones de singleton, es decir, Singleton ... los miembros estáticos pueden referirse a typeof (T) en mensajes de error o en cualquier otro lugar que se necesite. – yoyo

+1

Hola. Realmente me gusta y aprecio esta respuesta porque me da una solución para encontrar el tipo de niño a partir de una función base estática. – Bill

0

EDITAR Este método voluntad sólo funciona cuando se implementa archivos PDB con el ejecutable/biblioteca, como markmnl me señaló.

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


método de utilidad, sólo tiene que llamar al método cuando se necesita, desde cada 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; 
} 
+0

StackTrace solo está disponible en compilaciones de depuración – markmnl

+0

No es correcto: StackTrace estará disponible cuando también implemente archivos .pdb en modo de lanzamiento. http://stackoverflow.com/questions/2345957/access-the-stacktrace-when-deploying-in-release-mode –

+1

Estoy corregido. No obstante, no es una buena idea entonces. – markmnl

0

Para mis propósitos, me gusta la idea de @ T-moty. Aunque utilicé la información del "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 @ Rob Leclerc desde arriba):

public class ChildA: Parent<ChildA> 
{ 
} 

public class ChildB: Parent<ChildB> 
{ 
} 

Trabajar con este patrón puede ser un reto, por ejemplo; ¿cómo se devuelve la clase base de una llamada a función?

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

¿O cuando tipo de fundición?

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

Por lo tanto, trato de evitarlo cuando puedo, y lo uso cuando debo. Si es necesario, sugeriría 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) {} 
} 

ya se puede (más) trabajar fácilmente con la BaseClass. Sin embargo, hay momentos, como mi situación actual, donde 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. Lamentablemente, mis clases base usan constructores de instancias.

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

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; 
    } 
} 
Cuestiones relacionadas