.NET: Détermine le type de «this» classe dans sa méthode statique

94

Dans une méthode non statique, je pourrais utiliser this.GetType()et cela renverrait le fichier Type. Comment puis-je obtenir la même chose Typedans une méthode statique? Bien sûr, je ne peux pas simplement écrire typeof(ThisTypeName)car il ThisTypeNamen'est connu que dans l'exécution. Merci!

Yegor
la source
16
Vous êtes dans un contexte STATIQUE et ne pouvez pas écrire typeof (ThisTypeName)? Comment?
Bruno Reis
1
Il n'y a rien de tel que «runtime» dans une méthode statique (en supposant que vous ne parlez pas d'un argument qui est passé à une méthode statique). Dans ce cas, vous pouvez simplement dire typeof (RelevantType).
Manish Basantani
2
Une méthode statique ne peut pas être virtuelle. Vous connaissez déjà le type.
Hans Passant
7
Il y aura de nombreuses classes dérivées d'une classe abstraite. La classe abstraite de base a un dictionnaire statique <Int, Type>. Ainsi, les classes dérivées «s'enregistrent» dans des constructeurs statiques (dic.Add (N, T)). Et oui, je connais le type :) Je suis juste un peu paresseux et n'aime pas remplacer le texte et je me demandais si «T» peut être déterminé à l'exécution. Veuillez excuser mon mensonge, car il fallait simplement simplifier la question. Et cela a fonctionné;) Il existe maintenant une solution acceptée. Merci.
Yegor
Une sous-classe hérite des méthodes statiques de sa superclasse, non? Ne serait-il pas logique qu'une méthode statique de superclasse soit utile pour toutes ses sous-classes? Statique signifie simplement sans instance, le principe du code commun dans une classe de base commune s'applique sûrement aux méthodes statiques ainsi qu'aux méthodes d'instance?
Matt Connolly

Réponses:

134

Si vous recherchez une doublure 1 équivalente aux this.GetType()méthodes statiques, essayez ce qui suit.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Bien que cela soit probablement beaucoup plus cher que la simple utilisation typeof(TheTypeName).

JaredPar
la source
1
Celui-ci fonctionne très bien. Merci :) Ce n'est pas si cher car on l'appellera assez rare.
Yegor
2
Entrase, par «cher» Jared signifie qu'ils sont coûteux pour le processeur, ce qui signifie généralement lent. Mais il a dit, "beaucoup plus cher" signifiant plus lent. Probablement pas du tout lent, à moins que vous ne conceviez un système de guidage de fusée.
Dan Rosenstark
1
J'ai vu GetCurrentMethod causer de sérieux problèmes de performances. Mais puisque vous obtenez juste le type, vous pouvez le mettre en cache.
Jonathan Allen
51
Cela renvoie toujours la classe qui implémente la méthode actuelle, et non la classe sur laquelle elle a été appelée dans le cas des sous-classes.
Matt Connolly
3
Je suppose qu'il est pratique d'éviter les erreurs si le code est jamais migré vers différents noms de classe ou quelque chose du genre, mais un bon outil de refactorisation devrait typeof(TheTypeName)quand même s'en occuper .
Nyerguds le
59

Il y a quelque chose que les autres réponses n'ont pas tout à fait clarifié, et qui est pertinent pour votre idée du type n'étant disponible qu'au moment de l'exécution.

Si vous utilisez un type dérivé pour exécuter un membre statique, le nom du type réel est omis dans le binaire. Par exemple, compilez ce code:

UnicodeEncoding.GetEncoding(0);

Maintenant, utilisez ildasm dessus ... vous verrez que l'appel est émis comme ceci:

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

Le compilateur a résolu l'appel à Encoding.GetEncoding- il n'y a aucune trace de UnicodeEncodinggauche. Cela rend votre idée du "type actuel" absurde, j'en ai peur.

Jon Skeet
la source
24

Une autre solution consiste à utiliser un type auto-référencé

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

Ensuite, dans la classe qui en hérite, je crée un type d'auto-référencement:

public class Child: Parent<Child>
{
}

Maintenant, le type d'appel typeof (TSelfReferenceType) à l'intérieur de Parent obtiendra et retournera le Type de l'appelant sans avoir besoin d'une instance.

Child.GetType();

-Rob

Rob Leclerc
la source
Je l'ai utilisé pour les modèles de singleton, c'est-à-dire Singleton <T> ... les membres statiques peuvent alors faire référence à typeof (T) dans les messages d'erreur ou partout ailleurs.
yoyo
1
Salut. J'aime vraiment et apprécie cette réponse car elle me permet de trouver le type enfant à partir d'une fonction de base statique.
Bill Software Engineer
1
Joli. Malheureusement, cela ne peut pas être fait en C # sans cette petite duplication de code.
Afficher le nom le
Il existe un autre exemple de cette solution de contournement sur stackoverflow.com/a/22532416/448568
Steven de Salas
6

Vous ne pouvez pas utiliser thisdans une méthode statique, ce n'est donc pas possible directement. Cependant, si vous avez besoin du type d'un objet, appelez- GetTypele simplement et faites de l' thisinstance un paramètre que vous devez passer, par exemple:

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

Cela semble cependant être une mauvaise conception. Êtes-vous sûr que vous avez vraiment besoin d'obtenir le type de l'instance elle-même dans sa propre méthode statique? Cela semble un peu bizarre. Pourquoi ne pas simplement utiliser une méthode d'instance?

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

Je ne comprends pas pourquoi vous ne pouvez pas utiliser typeof (ThisTypeName). S'il s'agit d'un type non générique, cela devrait fonctionner:

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

S'il s'agit d'un type générique, alors:

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

Est-ce que je rate quelque chose d'évident ici?

Tarydon
la source
7
Cela ne fonctionnera pas si vous créez une classe Bar dérivée de Foo et que la classe hérite de Method1 - alors un appel à Bar.Method1 traitera toujours typeof (Foo) qui est faux. Le Method1 hérité doit en quelque sorte savoir qu'il est dérivé dans Bar, puis obtenir le typeof (Bar).
JustAMartin
0

Lorsque votre membre est statique, vous saurez toujours de quel type il fait partie au moment de l'exécution. Dans ce cas:

class A
{
  public static int GetInt(){}

}
class B : A {}

Vous ne pouvez pas appeler (modifier: apparemment, vous pouvez, voir le commentaire ci-dessous, mais vous appelleriez toujours en A):

B.GetInt();

comme le membre est statique, il ne joue pas de rôle dans les scénarios d'héritage. Ergo, vous savez toujours que le type est A.

Teun D
la source
4
Vous pouvez appeler B.GetInt () - du moins, si ce n'était pas privé - mais la compilation le traduira en un appel à A.GetInt (). Essayez-le!
Jon Skeet
Remarque sur le commentaire de Jon: la visibilité par défaut en C # est privée, donc votre exemple ne fonctionne pas.
Dan Rosenstark
0

Pour mes besoins, j'aime l'idée de @ T-moty. Même si j'utilise des informations de "type d'auto-référencement" depuis des années, il est plus difficile de référencer la classe de base plus tard.

Par exemple (en utilisant l'exemple @Rob Leclerc ci-dessus):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Travailler avec ce modèle peut être difficile, par exemple; comment renvoyez-vous la classe de base à partir d'un appel de fonction?

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

Ou lors du casting de type?

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

Alors, j'essaye de l'éviter quand je le peux et de l'utiliser quand je le dois. Si vous le devez, je vous suggère de suivre ce modèle:

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

Vous pouvez désormais (plus) facilement travailler avec le BaseClass. Cependant, il y a des moments, comme ma situation actuelle, où exposer la classe dérivée, à partir de la classe de base, n'est pas nécessaire et utiliser la suggestion de @ M-moty pourrait être la bonne approche.

Cependant, l'utilisation du code de @ M-moty ne fonctionne que tant que la classe de base ne contient aucun constructeur d'instance dans la pile d'appels. Malheureusement, mes classes de base utilisent des constructeurs d'instances.

Par conséquent, voici ma méthode d'extension qui prend en compte les constructeurs d'instance de la classe de 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
la source
0

EDIT Cette méthode ne fonctionnera que lorsque vous déployez des fichiers PDB avec l'exécutable / bibliothèque, comme markmnl m'a fait remarquer.

Sinon, ce sera un énorme problème à détecter: fonctionne bien en développement, mais peut-être pas en production.


Méthode utilitaire, appelez simplement la méthode quand vous en avez besoin, de chaque endroit de votre code:

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
la source
1
StackTrace n'est disponible que dans les versions de débogage
markmnl
Non correct: StackTrace sera disponible lorsque vous déployez également des fichiers .pdb en mode version. stackoverflow.com/questions/2345957/…
T-moty
Je vois ce que tu veux dire. Le fait qu'une méthode ne fonctionne que lorsque les fichiers PDB sont déployés n'est pas acceptable. Je vais éditer la réponse
T-moty