Получить тип производного класса из статического метода базового класса

Мне хотелось бы получить тип производного класса из статического метода его базового класса.

Как это можно сделать?

Спасибо!

class BaseClass {
  static void Ping () {
     Type t = this.GetType(); // should be DerivedClass, but it is not possible with a static method
  }
}
class DerivedClass : BaseClass {}

// somewhere in the code
DerivedClass.Ping();

Ответ 1

Если я не ошибаюсь, код, испущенный для BaseClass.Ping() и DerivedClass.Ping(), будет таким же, поэтому статический метод, не дающий никаких аргументов, не будет работать. Попробуйте передать тип в качестве аргумента или через общий тип типа (на котором вы можете принудительно установить ограничение наследования).

class BaseClass {
    static void Ping<T>() where T : BaseClass {
        Type t = typeof(T);
    }
}

Вы бы назвали это следующим образом:

BaseClass.Ping<DerivedClass>();

Ответ 2

Это можно легко выполнить с помощью любопытно повторяющегося шаблона шаблона

class BaseClass<T>
    where T : BaseClass<T>
{
    static void SomeMethod() {
        var t = typeof(T);  // gets type of derived class
    }
}

class DerivedClass : BaseClass<DerivedClass> {}

вызов метода:

DerivedClass.SomeMethod();

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

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

И по шаблонам я, конечно, имею в виду дженерики.

Ответ 3

Статический метод определяется типом. Нет "this". Вам нужно будет сделать это методом экземпляра, вместо этого:

class BaseClass {
    public void Ping() {
        Type t = this.GetType(); // This will work, since "this" has meaning here...
    }

Затем вы можете:

class DerivedClass : BaseClass {}

DerivedClass instance = new DerivedClass();
instance.Ping();

Ответ 4

Короче, вот что я пытался ответить здесь. Он использует один интерфейс для унификации всех производных классов и позволяет всем им вызвать один и тот же статический метод из базового класса без передачи типа.

public interface IDerived
{
}
public class BaseClass<T> where T : IDerived
{
    public static void Ping()
    {
        //here you have T = the derived type
    }
}
public class DerivedClass : BaseClass<DerivedClass>, IDerived
{
    //with : BaseClass<DerivedClass> we are defining the T above
}
public class ExampleApp
{
    public void Main()
    {
        //here we can call the BaseClass static method through DerivedClass
        //and it will know what Type calls it
        DerivedClass.Ping();    
    }
}

Ответ 5

Невозможно получить производный класс из статического метода. В качестве примера, чтобы проиллюстрировать, почему, представьте, что BaseClass имеет 2 подкласса - DerivedClass и AnotherDerivedClass - какой из них нужно вернуть? В отличие от полиморфных нестатических методов нет никакой связи с производным классом, вызывающим статический метод в базовом классе - тип времени компиляции и тип времени выполнения одинаковы со статическим вызовом метода.

Вы можете либо сделать метод нестатичным, чтобы затем получить правильный тип с помощью полиморфизма, либо создать "переопределения" статического метода в подклассах, например.

class DerivedClass : BaseClass
{
   void Ping() { 
     BaseClass.Ping();
     // or alternatively
     BaseClass.Ping(Type.GetType("DerivedClass"));
   }
}

Затем ваш код клиента может вызвать метод в производном классе, чтобы явно указать, хочет ли он получить версию производного класса. При необходимости вы также можете передать тип DerivedClass в качестве параметра метода базового класса, чтобы предоставить контекст, который был вызван методом через производный класс.

Ответ 6

Просто предположение (не проверено)

Type t = MethodBase.GetCurrentMethod().DeclaringType;

Ответ 7

Почему бы просто не использовать уже существующие методы?

Если у вас

class BaseClass {}
partial class DerivedClass : BaseClass {}

Вы можете посмотреть

DerivedClass.GetType().BaseType;

Ответ 8

Я думаю, что для этого случая будет работать следующее (и несколько подобных в другом месте на SO). Перф не будет слишком хорош, но если это нечасто, это не будет проблемой.

Создайте stacktrace и проанализируйте его поиск производного класса. В общем случае это не было бы слишком надежным или даже не работало, но в конкретных случаях, как в OP, я считаю, что это будет работать нормально. В Powershell:

$strace = (new-object diagnostics.stacktrace).tostring()
#
$frames = $strace -split "   at "
$typesFromFrames = $frames | select -skip 1| # skip blank line at the beginning
   % { ($_ -split "\(",2)[0]} |                 # Get rid of parameters string
   % {$_.substring(0,$_.lastindexof("."))} |    # Get rid of method name
   $ {$_ -as [type]}
#
# In powershell a lot of the frames on the stack have private classes
#  So $typesFromFrames.count is quite a bit smaller than $frames.count
#  For the OP, I don't think this will be a problem because:
#   1. PS isn't being used
#   2. The derived class in the OP isn't private 
#        (if it is then tweaks will be needed)
#
$derivedOnStack = $typesFromFrames | ? { $_.issubclassof( [BaseClass])}

Надеюсь, что в $outputOnStack будет только один элемент, но он будет зависеть от особенностей приложения. Некоторые эксперименты потребуются.