.NET: Determine o tipo de “esta” classe em seu método estático

94

Em um método não estático, eu poderia usar this.GetType()e ele retornaria o Type. Como posso obter o mesmo Typeem um método estático? Claro, eu não posso simplesmente escrever typeof(ThisTypeName)porque ThisTypeNameé conhecido apenas em tempo de execução. Obrigado!

Yegor
fonte
16
Você está em um contexto ESTÁTICO e não pode escrever typeof (ThisTypeName)? Quão?
Bruno Reis
1
Não há nada como 'runtime' dentro de um método estático (assumindo que você não está falando sobre um argumento que é passado para um método estático). Nesse caso, você pode simplesmente dizer typeof (RelevantType).
Manish Basantani
2
Um método estático não pode ser virtual. Você já conhece o tipo.
Hans Passant
7
Haverá muitas classes derivadas de uma abstrata. A classe abstrata base possui um dicionário estático <Int, Type>. Assim, as classes derivadas “registram-se” em construtores estáticos (dic.Add (N, T)). E sim, eu conheço o tipo :) Sou apenas um pouco preguiçoso e não gosto de substituir o texto e queria saber se “T” pode ser determinado em tempo de execução. Desculpem a minha mentira, pois foi necessário apenas simplificar a pergunta. E funcionou;) Existe uma solução aceita agora. Obrigado.
Yegor
Uma subclasse herda métodos estáticos de sua superclasse, não? Não faria sentido um método estático da superclasse ser útil para todas as suas subclasses? Estático significa simplesmente sem uma instância. Certamente o princípio do código comum em uma classe base comum se aplica a métodos estáticos e também a métodos de instância.
Matt Connolly

Respostas:

134

Se você está procurando um liner 1 que é equivalente aos this.GetType()métodos estáticos, tente o seguinte.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Embora isso seja provavelmente muito mais caro do que apenas usar typeof(TheTypeName).

JaredPar
fonte
1
Este funciona bem. Obrigado :) Não é tão caro porque será chamado de raro.
Yegor
2
Entrase, por Jared "caro" significa que eles são caros para o processador, geralmente significando lento. Mas ele disse, "muito mais caro", o que significa mais lento. Provavelmente nem um pouco lento, a menos que você esteja projetando um sistema de orientação de foguete.
Dan Rosenstark
1
Já vi GetCurrentMethod causar sérios problemas de desempenho. Mas, como você está apenas obtendo o tipo, pode armazená-lo em cache.
Jonathan Allen
51
Isso sempre retorna a classe que implementa o método atual, não a classe em que foi chamada no caso de subclasses.
Matt Connolly
3
Eu acho que é útil evitar erros se o código for migrado para nomes de classes diferentes ou algo assim, mas uma boa ferramenta de refatoração deve cuidar disso de typeof(TheTypeName)qualquer maneira.
Nyerguds
59

Há algo que as outras respostas não esclareceram e que é relevante para a sua ideia do tipo estar disponível apenas em tempo de execução.

Se você usar um tipo derivado para executar um membro estático, o nome do tipo real será omitido no binário. Portanto, por exemplo, compile este código:

UnicodeEncoding.GetEncoding(0);

Agora use ildasm nele ... você verá que a chamada é emitida assim:

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

O compilador resolveu a chamada para Encoding.GetEncoding- não há vestígios de UnicodeEncodingesquerda. Isso torna a sua ideia do "tipo atual" absurda, infelizmente.

Jon Skeet
fonte
24

Outra solução é usar um 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);
    }
}

Então, na classe que o herda, faço um tipo de autorreferência:

public class Child: Parent<Child>
{
}

Agora, o tipo de chamada typeof (TSelfReferenceType) dentro de Parent obterá e retornará o Type do chamador sem a necessidade de uma instância.

Child.GetType();

-Roubar

Rob Leclerc
fonte
Eu usei isso para padrões singleton, ou seja, Singleton <T> ... membros estáticos podem então referir-se a typeof (T) em mensagens de erro ou onde mais for necessário.
yoyo
1
Oi. Eu realmente gosto e aprecio esta resposta porque me dá uma solução alternativa para encontrar o tipo filho de uma função de base estática.
Bill Software Engineer
1
Agradável. É muito triste que em C # isso não possa ser feito sem essa pequena duplicação de código, no entanto.
Nome de exibição
Há outro exemplo dessa solução alternativa em stackoverflow.com/a/22532416/448568
Steven de Salas
6

Você não pode usar thisem um método estático, então isso não é possível diretamente. Porém, se você precisa do tipo de algum objeto, basta chamá GetType-lo e fazer da thisinstância um parâmetro que você deve passar, por exemplo:

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

No entanto, este parece ser um design ruim. Tem certeza de que realmente precisa obter o tipo da própria instância dentro de seu próprio método estático? Isso parece um pouco bizarro. Por que não usar apenas um método de instância?

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

Não entendo por que você não pode usar typeof (ThisTypeName). Se este for um tipo não genérico, isso deve funcionar:

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

Se for um tipo genérico, então:

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

Estou perdendo algo óbvio aqui?

Tarydon
fonte
7
Isso não funcionará se você criar uma classe Bar derivada de Foo e, em seguida, a classe herdar Method1 - então uma chamada para Bar.Method1 ainda processará typeof (Foo) que está errado. O Method1 herdado deve de alguma forma saber que está disponível em Bar e, em seguida, obter o typeof (Bar).
JustAMartin
0

Quando seu membro é estático, você sempre saberá de que tipo ele faz parte em tempo de execução. Nesse caso:

class A
{
  public static int GetInt(){}

}
class B : A {}

Você não pode ligar (editar: aparentemente, você pode, veja o comentário abaixo, mas você ainda estaria ligando em A):

B.GetInt();

como o membro é estático, ele não faz parte dos cenários de herança. Portanto, você sempre sabe que o tipo é A.

Teun D
fonte
4
Você pode chamar B.GetInt () - pelo menos, você poderia se não fosse privado - mas a compilação irá traduzi-lo em uma chamada para A.GetInt (). Tente!
Jon Skeet
Nota sobre o comentário de Jon: a visibilidade padrão em C # é privada, portanto, seu exemplo não funciona.
Dan Rosenstark
0

Para os meus propósitos, gosto da ideia de @T-moty. Embora eu tenha usado informações de "tipo de autorreferência" por anos, referenciar a classe base é mais difícil de fazer mais tarde.

Por exemplo (usando o exemplo de @Rob Leclerc acima):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Trabalhar com esse padrão pode ser desafiador, por exemplo; como você retorna a classe base de uma chamada de função?

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

Ou quando digitar o casting?

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

Portanto, tento evitá-lo quando posso e usá-lo quando preciso. Se necessário, sugiro que siga este padrão:

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

Agora você pode (mais) trabalhar facilmente com o BaseClass. No entanto, há momentos, como minha situação atual, em que expor a classe derivada, de dentro da classe base, não é necessário e usar a sugestão de @M-moty pode ser a abordagem certa.

No entanto, usar o código de @M-moty funciona apenas enquanto a classe base não contiver nenhum construtor de instância na pilha de chamadas. Infelizmente, minhas classes base usam construtores de instância.

Portanto, aqui está meu método de extensão que leva em consideração os construtores de 'instância' da classe 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
fonte
0

EDITAR Este método funcionará apenas quando você implantar arquivos PDB com o executável / biblioteca, como markmnl apontou para mim.

Caso contrário, será um grande problema a ser detectado: funciona bem no desenvolvimento, mas talvez não na produção.


Método utilitário, basta chamar o método quando precisar, de qualquer lugar do seu 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
fonte
1
StackTrace está disponível apenas em compilações de depuração
markmnl
Incorreto: StackTrace estará disponível quando você também implantar arquivos .pdb no modo de liberação. stackoverflow.com/questions/2345957/…
T-moty
Compreendi seu argumento. Não é aceitável que um método funcione apenas quando os arquivos PDB são implantados. Vou editar a resposta
T-moty