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

Хорошо... в Objective C вы можете создать подкласс из статического метода в базовом классе с помощью "new this()", потому что в статическом методе "это" относится к классу, а не к экземпляру. Это была чертовски крутая находка, когда я впервые нашел ее, и я часто ей пользовался.

Однако в C# это не работает. Черт!

Итак... кто-нибудь знает, как я могу "обновить" подкласс из статического метода базового класса?

Что-то вроде этого...

public class MyBaseClass{

    string name;

    public static Object GimmeOne(string name){

     // What would I replace 'this' with in C#?
        return new this(name); 

    }

    public MyBaseClass(string name){
        this.name = name;
    }

}

// No need to write redundant constructors
   public class SubClass1 : MyBaseClass{ }
   public class SubClass2 : MyBaseClass{ }
   public class SubClass3 : MyBaseClass{ }

SubClass1 foo = SubClass1.GimmeOne("I am Foo");

И да, я знаю, что могу (и обычно так бы и сделал) просто использовать конструкторы напрямую, но у нас есть конкретная необходимость вызывать общий член базового класса, поэтому я и спрашиваю. Опять же, Objective C позволяет мне это сделать. Надеюсь, С# тоже.

Так... есть берущие?


person Mark A. Donohoe    schedule 09.09.2010    source источник
comment
Я также привык к Objective-C и пытаюсь понять, как это сделать на C++. Я думаю, что если С# не может этого сделать, то и С++ не сможет :(   -  person Zaky German    schedule 23.04.2013


Ответы (2)


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

public class MyBaseClass
{
    public string Name { get; private set; }

    public static T GimmeOne<T>(string name) where T : MyBaseClass, new()
    {
        return new T() { Name = name };
    }

    protected MyBaseClass()
    {
    }

    protected MyBaseClass(string name)
    {
        this.Name = name;
    }
}

Ограничение new() говорит, что существует конструктор без параметров, которого у вас нет, но мы делаем его приватным, чтобы скрыть его от потребителей. Тогда его можно было бы вызвать следующим образом:

var foo = SubClass1.GimmeOne<SubClass1>("I am Foo");
person Steve Michelotti    schedule 09.09.2010
comment
Чувак... Я корю себя! О дженериках даже не думала, а пользуюсь ими постоянно! Это проклятое переключение между C# и C++ (и Objective-C[++] в этом отношении!) Быстрый вопрос, хотя... Вместо ограничения 'new()' и 'return new T(){Name =имя};' не могли бы вы вместо этого сделать ограничение типа «MyBaseClass», а затем просто вызвать «return new T (name);»? Таким образом, вы по-прежнему можете в полной мере использовать правильный конструктор, а не простое присваивание. Кстати, в вашем коде, почему вам все еще нужен «защищенный MyBaseClass (имя строки)», не говоря уже о том, почему вы изменили его с общедоступного? - person Mark A. Donohoe; 09.09.2010
comment
Возможно, я только что что-то вспомнил... разве вам НЕ ДОЛЖЕН быть конструктор "new()" для "нового" чего-то? Я так понимаю, нет способа установить ограничение «новое (строка)»? ...или есть? - person Mark A. Donohoe; 09.09.2010
comment
Правильно - вы ДОЛЖНЫ иметь ограничение new(), чтобы что-то новое. - person Steve Michelotti; 09.09.2010
comment
Второй конструктор все еще может быть общедоступным - да, в любом случае все в порядке. Я сделал его защищенным, поскольку оказалось, что цель вашего кода заключалась в том, чтобы потребители создавали объект с помощью фабричного метода, а не конструктора. Кроме того, если вы когда-нибудь измените MyBaseClass на абстрактный, то в соответствии с лучшими практиками конструктор должен быть защищенным, а не общедоступным (поскольку потребитель все равно не может напрямую создавать экземпляр конструктора абстрактного класса). - person Steve Michelotti; 09.09.2010
comment
Ну, у нас был этот конструктор базового класса только потому, что мы намеревались «подобрать» его в подклассах, как мы можем в C++, но поскольку мы не можем, ваш код работает нормально, и мы, скорее всего, можем просто удалить его. На самом деле, мы, вероятно, изменили бы его на «защищенный void Init(string name)» и вызвали бы его, а не присваивание конструктора, чтобы мы могли а) делать больше, чем просто присваивание, и б) разрешать подклассам переопределять его, если это необходимо. В любом случае, я принял ваш ответ и тоже проголосовал за него. Чертовски хорошая вещь, мой друг! Очень! - person Mark A. Donohoe; 09.09.2010

Извините, вы не можете этого сделать. C# морально против статического наследования методов. Этот метод GimmeOne никогда не будет иметь другого типа, кроме MyBaseClass, и вызов его из SubClass1 не имеет значения — это все еще «на самом деле» вызов MyBaseClass. Библиотеки Reflection могут сделать эту конструкцию, но вы никогда не получите из нее ничего, кроме MyBaseClass.

Если вы вызываете статический метод, вероятно, вы знаете, из какого подкласса вы его вызываете. Создайте другой фабричный метод для каждого подкласса. Если вы на самом деле пытаетесь сделать это по экземпляру, вам, вероятно, следует использовать нестатический метод виртуальной фабрики (который будет автоматически вызывать наиболее производную форму функции, что, вероятно, вам и нужно).

person Adam Norberg    schedule 09.09.2010