Подтвердить что ты не робот

Ссылаться на тип "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();
}

Какой лучший способ сделать это?

4b9b3361

Ответ 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, но я думаю, что это выходит за рамки вопроса.