.NET: Визначте тип класу "this" у його статичному методі


94

У нестатичному методі я міг би використовувати, this.GetType()і він повертав би Type. Як я можу отримати те саме Typeв статичному методі? Звичайно, я не можу просто писати, typeof(ThisTypeName)бо ThisTypeNameце відомо лише під час роботи. Дякую!


16
Ви перебуваєте в СТАТИЧНОМУ контексті і не можете написати typeof (ThisTypeName)? Як?
Бруно Рейс,

1
Всередині статичного методу немає нічого подібного до "середовища виконання" (припускаючи, що ви не говорите про аргумент, який передається статичному методу). У цьому випадку ви можете просто сказати typeof (RelevantType).
Маніш Басантані

2
Статичний метод не може бути віртуальним. Ви вже знаєте тип.
Ганс Пассант,

7
Буде багато похідних класів з абстрактного. Базовий абстрактний клас має статичний словник <Int, Type>. Отже похідні класи «реєструються» у статичних конструкторах (dic.Add (N, T)). І так, я знаю тип :) Я просто трохи лінивий і не люблю замінювати текст, і мені було цікаво, чи можна визначити “T” під час роботи. Будь ласка, вибачте за мою брехню, бо це потрібно було просто для спрощення питання. І це спрацювало;) Зараз є прийняте рішення. Дякую.
Єгор

Підклас успадковує статичні методи свого суперкласу, ні? Чи не має сенсу статичний метод суперкласу бути корисним для всіх його підкласів? Статичний просто означає, що без екземпляра, безумовно, принцип загального коду в загальному базовому класі застосовується як до статичних методів, так і до методів екземплярів?
Метт Конноллі,

Відповіді:


134

Якщо ви шукаєте вкладиш 1, еквівалентний this.GetType()статичним методам, спробуйте наступне.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Хоча це, швидше за все, набагато дорожче, ніж просто використання typeof(TheTypeName).


1
Цей працює чудово. Дякую :) Це не так дорого, тому що його називатимуть досить рідко.
Єгор

2
Entrase, під "дорогим" Jared означає, що вони дорогі для процесора, як правило, означає повільний. Але він сказав, що "набагато дорожче" означає повільніше. Можливо, зовсім не повільно, якщо ви не розробляєте систему ракетного наведення.
Dan Rosenstark,

1
Я бачив, як GetCurrentMethod спричиняє серйозні проблеми з продуктивністю. Але оскільки ви лише отримуєте тип, ви можете його кешувати.
Джонатан Аллен

51
Це завжди повертає клас, який реалізує поточний метод, а не клас, до якого він був викликаний у разі підкласів.
Метт Конноллі,

3
Думаю, що це зручно, щоб уникнути помилок, якщо код коли-небудь перенесений на різні назви класів чи щось інше, але хороший інструмент рефакторингу typeof(TheTypeName)все одно повинен подбати про це .
Nyerguds,

59

Є щось, що в інших відповідях не зовсім з’ясовано, і що стосується вашої ідеї про тип, доступний лише під час виконання.

Якщо ви використовуєте похідний тип для виконання статичного члена, справжнє ім'я типу пропускається в двійковому файлі. Так, наприклад, скомпілюйте цей код:

UnicodeEncoding.GetEncoding(0);

Тепер використовуйте на ньому ildasm ... ви побачите, що дзвінок передається так:

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

Компілятор вирішив виклик Encoding.GetEncoding- від нього не залишилося й сліду UnicodeEncoding. Це боїться вашої ідеї "поточного типу" безглуздою.


24

Іншим рішенням є використання самореференційного типу

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

Потім у класі, який успадковує його, я роблю тип самонасилання:

public class Child: Parent<Child>
{
}

Тепер тип виклику typeof (TSelfReferenceType) всередині Parent отримає і поверне Тип абонента без потреби екземпляра.

Child.GetType();

-Роб


Я використовував це для одиночних шаблонів, тобто Singleton <T> ... статичні члени можуть потім посилатися на typeof (T) у повідомленнях про помилки або де б це ще не було потрібно.
yoyo

1
Привіт. Мені дуже подобається і ціную цю відповідь, тому що вона дає мені змогу знайти дочірній тип із статичної базової функції.
Інженер-програміст Білла,

1
Хороший. Дуже сумно, що в C # цього не можна зробити без цього маленького дублювання коду.
Відображуване ім’я

Є ще один приклад цього обхідного шляху
Стівен де Салас

6

Ви не можете використовувати thisстатичний метод, тому це неможливо безпосередньо. Однак, якщо вам потрібен тип якогось об'єкта, просто зателефонуйте GetTypeйому та зробіть thisекземпляр параметром, який потрібно передати, наприклад:

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

Однак це здається поганим дизайном. Ви впевнені, що вам дійсно потрібно отримати тип самого екземпляра всередині власного статичного методу? Це здається трохи химерним. Чому б просто не використовувати метод екземпляра?

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

3

Я не розумію, чому ви не можете використовувати typeof (ThisTypeName). Якщо це не загальний тип, то це має спрацювати:

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

Якщо це загальний тип, то:

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

Мені чогось тут не вистачає?


7
Це не буде працювати, якщо ви створите клас Bar, похідний від Foo, а потім клас успадковує Method1 - тоді виклик Bar.Method1 все одно буде обробляти typeof (Foo), що є неправильним. Успадкований Метод1 повинен якось знати, що він виробляється в Bar, а потім отримати typeof (Bar).
JustAMartin

0

Коли ваш член є статичним, ви завжди будете знати, до якого типу він входить під час виконання. В цьому випадку:

class A
{
  public static int GetInt(){}

}
class B : A {}

Ви не можете зателефонувати (редагувати: мабуть, ви можете, див. Коментар нижче, але ви все одно будете телефонувати в A):

B.GetInt();

оскільки член є статичним, він не грає участі у сценаріях успадкування. Ерго, ти завжди знаєш, що тип А.


4
Ви можете зателефонувати B.GetInt () - принаймні, ви могли б, якби він не був приватним, - але компіляція переведе його у виклик A.GetInt (). Спробуй це!
Джон Скіт,

Примітка щодо коментаря Джона: видимість за замовчуванням у C # є приватною, отже, ваш приклад не працює.
Dan Rosenstark,

0

Для моїх цілей мені подобається ідея @ T-moty. Навіть незважаючи на те, що я роками використовував інформацію "типу самозв'язку", посилання на базовий клас важче зробити пізніше.

Наприклад (за допомогою прикладу @Rob Leclerc зверху):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Наприклад, робота з цим шаблоном може бути складною; як повернути базовий клас із виклику функції?

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

Або при кастингу типу?

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

Отже, я намагаюся уникати цього, коли можу, і використовую, коли повинен. Якщо потрібно, я б запропонував дотримуватися наступного:

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

Тепер ви можете (легше) працювати з BaseClass. Однак бувають випадки, як у моїй нинішній ситуації, коли викриття похідного класу з базового класу не потрібно, і використання пропозиції @ M-moty може бути правильним підходом.

Однак використання коду @ M-moty працює лише до тих пір, поки базовий клас не містить конструкторів екземплярів у стеку викликів. На жаль, мої базові класи використовують конструктори екземплярів.

Отже, ось мій метод розширення, який враховує конструктори базового класу 'instance':

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

0

EDIT Цей метод буде працювати лише тоді, коли ви розгортаєте PDB-файли з виконуваним файлом / бібліотекою, як мені вказав markmnl .

Інакше буде виявлено величезну проблему: вона добре працює у розробці, але, можливо, не у виробництві.


Утилітний метод, просто викликайте метод, коли вам потрібно, з будь-якого місця вашого коду:

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

1
StackTrace доступний лише у збірках для налагодження
markmnl

Неправильно: StackTrace буде доступним, коли ви також розгорнете файли .pdb у режимі випуску. stackoverflow.com/questions/2345957 / ...
T-Moty

Я зрозумів вашу думку. Неприпустимо, щоб метод працював лише при розгортанні файлів PDB. Я відредагую відповідь
T-moty
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.