Ссылаться на тип "self" в статическом методе - С#
Я пытаюсь создать статический метод, который возвращает экземпляр класса, например:
class A {
public static A getInstance() {
return new A();
}
}
Проблема, с которой я столкнулась, заключается в том, что если у меня есть подкласс B, полученный из A, я бы хотел, чтобы B.getInstance() возвращал экземпляр B, а не A. В мире PHP вы могли бы использовать ключевое слово "self" "чтобы ссылаться на ваш собственный тип, так что ваш getInstance() будет выглядеть так:
public static function getInstance() {
return new self();
}
Какой лучший способ сделать это?
Ответы
Ответ 1
Вы можете скрыть и, таким образом, переопределить метод getInstance в наследующем типе, например:
class B : A {
public static new B getInstance() {
return new B();
}
}
Ключевое слово new
указывает, что скрытие унаследованного метода было преднамеренным, иначе вы получите предупреждение компилятора.
Полный пример:
using System;
class A {
public static A getInstance() { return new A(); }
public void PrintSelf() {Console.WriteLine(this.GetType().Name);}
}
class B : A {
public static new B getInstance() {
return new B();
}
}
public class MyClass {
public static void Main() {
A a = A.getInstance();
B b = B.getInstance();
a.PrintSelf();
b.PrintSelf();
}
}
Ответ 2
Вы не можете, в принципе. Если вызов статического члена, который объявлен только в базовом классе, фактически выражается в терминах производного класса, например:
// Urgh
Encoding ascii = ASCIIEncoding.ASCII;
// Worse yet (and yes, I've seen this)
Encoding ascii = UTF8Encoding.ASCII;
то компилятор молча преобразует это значение в:
Encoding ascii = Encoding.ASCII;
Тот факт, что исходный исходный код содержит имя производного класса, не сохраняется в скомпилированном коде, поэтому он не может быть задействован во время выполнения.
Ответ 3
Как насчет использования generics для обеспечения типа, который вы создаете:
public static T getInstance<T>() where T : A, new()
{
return new T();
}
Ответ 4
Так как другие говорят, что нет способа сделать это, я просто предлагаю CRTP. У этого есть свои недостатки (у Эрика Липперта есть сообщение об этом здесь где-то), но он решит эту проблему.
class Base<T> where T : Base<T>, new() {
public static T GetInstance() {
return new T();
}
}
class Derived : Base<Derived> {
}
Derived d = Derived.GetInstance();
Это делает трудным использование Base<T>
. Лучше всего, если Base<T>
является абстрактным. Вы также можете создать такой класс, который можно фактически создать и использовать:
class Base : Base<Base> {
}
Это выглядит странно, но это работает. К сожалению, Base
и Derived
больше не будут связаны (кроме Base<T>
), поэтому вы не можете их отличать как таковые.
Ответ 5
сделать это статически - вам просто нужно использовать фактическое имя.
Однако, я буду держать пари, что PHP делает это динамически (т.е. с отражением) - в этом случае вы всегда можете использовать GetCurrentMethod
, чтобы получить информацию о методе и его охватывающем типе.
Таким образом вы можете сделать что-то вроде:
using System.Reflection;
class Test
{
static object MakeTest()
{
return Activator.CreateInstance(
MethodBase.GetCurrentMethod().DeclaringType);
}
void Hello() { Console.WriteLine("Hi!"); }
static void Main()
{
dynamic test = MakeTest();
test.Hello();
}
}
Ответ 6
Вы не можете переопределить статическую функцию-член в производном классе. Если вы выбираете статический подход, вам нужно просто добавить тот же метод к классу B.
Конечно, вы можете использовать и другие шаблоны проектирования, например Factory, но я думаю, что это выходит за рамки вопроса.