.NET: определить тип «этого» класса в его статическом методе.

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, слово «дорогие» Джареда означает, что они дорогие для процессора, обычно это означает медленные. Но он сказал: «намного дороже», что означает медленнее. Наверное, совсем не медленный, если только вы не разрабатываете систему наведения ракеты.
Дэн Розенстарк
1
Я видел, как GetCurrentMethod вызывает серьезные проблемы с производительностью. Но поскольку вы только получаете тип, вы можете его кешировать.
Джонатан Аллен
51
Это всегда возвращает класс, реализующий текущий метод, а не класс, к которому он был вызван в случае подклассов.
Мэтт Коннолли
3
Я думаю, это удобно, чтобы избежать ошибок, если код когда-либо переносится на другие имена классов или что-то в этом роде, но хороший инструмент рефакторинга typeof(TheTypeName)все равно должен позаботиться об этом .
Nyerguds 02
59

Есть кое-что, что другие ответы не совсем прояснили, и что имеет отношение к вашей идее о том, что тип доступен только во время выполнения.

Если вы используете производный тип для выполнения статического члена, настоящее имя типа в двоичном файле опускается. Так, например, скомпилируйте этот код:

UnicodeEncoding.GetEncoding(0);

Теперь используйте ildasm ... вы увидите, что вызов излучается следующим образом:

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

Компилятор разрешил вызов Encoding.GetEncoding- не осталось никаких следов UnicodeEncoding. Боюсь, это делает ваше представление о "нынешнем типе" бессмысленным.

Джон Скит
источник
Перенесемся на 10 лет вперед, и почему в C # до сих пор нет виртуальной статики? ;) обычно они никому не нужны ... но бывают редкие случаи, когда они могут пригодиться;)
marchewek
@marchewek: Я бы сказал, очень, очень редко - достаточно редко, чтобы команда C # всегда находила более полезные занятия. (Не то чтобы они все это время сидели без дела ...)
Джон Скит,
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 будет получать и возвращать Type вызывающего абонента без необходимости в экземпляре.

Child.GetType();

-Роб

Роб Леклерк
источник
Я использовал это для одноэлементных шаблонов, то есть Singleton <T> ... статические члены могут затем ссылаться на typeof (T) в сообщениях об ошибках или в любом другом месте, где это необходимо.
йойо
1
Здравствуй. Мне очень нравится и ценен этот ответ, потому что он дает мне обходной путь, чтобы найти дочерний тип из статической базовой функции.
Bill Software Engineer
1
Хороший. Жаль, что в C # это невозможно сделать без небольшого дублирования кода.
Отображаемое имя
Есть еще один пример этого обходного пути на stackoverflow.com/a/22532416/448568
Стивен де Салас,
Оба (этот и тот, который связан Стивеном) не будут работать для наследников разработчика базового класса ... Внуки будут иметь тип Child ... Жаль, что C # не имеет виртуальной статики;)
marchewek
5

Вы не можете использовать 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), что неверно. Унаследованный Method1 должен каким-то образом знать, что он создан в Bar, а затем получить typeof (Bar).
JustAMartin
0

Когда ваш член статический, вы всегда будете знать, к какому типу он принадлежит во время выполнения. В таком случае:

class A
{
  public static int GetInt(){}

}
class B : A {}

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

B.GetInt();

поскольку член является статическим, он не участвует в сценариях наследования. Следовательно, вы всегда знаете, что это тип А.

Teun D
источник
4
Вы можете вызвать B.GetInt () - по крайней мере, вы могли бы, если бы он не был закрытым, - но компиляция преобразует его в вызов A.GetInt (). Попытайся!
Джон Скит
Обратите внимание на комментарий Джона: видимость по умолчанию в C # является частной, поэтому ваш пример не работает.
Дэн Розенстарк
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 работает только до тех пор, пока базовый класс не содержит конструкторов экземпляров в стеке вызовов. К сожалению, в моих базовых классах используются конструкторы экземпляров.

Поэтому вот мой метод расширения, который учитывает конструкторы экземпляра базового класса:

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

ИЗМЕНИТЬ Эти методы будут работать только тогда, когда вы развертываете файлы 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;
}
оборота T-moty
источник
1
StackTrace доступен только в отладочных сборках
markmnl
Неправильно: StackTrace будет доступен, когда вы также развернете файлы .pdb в режиме выпуска. stackoverflow.com/questions/2345957/…
T-moty
Японял твою точку зрения. Неприемлемо, чтобы метод работал только тогда, когда файлы PDB развернуты.
Отредактирую