Я понимаю, что наследование статических методов не поддерживается в C #. Я также прочитал ряд обсуждений (в том числе здесь), в которых разработчики заявляют о необходимости этой функциональности, на что обычно отвечают: «Если вам нужно статическое наследование членов, в вашем дизайне есть изъян».
Хорошо, учитывая, что ООП не хочет, чтобы я даже думал о статическом наследовании, я должен сделать вывод, что моя очевидная потребность в нем указывает на ошибку в моем дизайне. Но я застрял. Я был бы очень признателен за помощь в решении этой проблемы. Вот вызов ...
Я хочу создать абстрактный базовый класс (назовем его Fruit), который инкапсулирует некоторый сложный код инициализации. Этот код нельзя поместить в конструктор, поскольку часть его будет зависеть от вызовов виртуальных методов.
Fruit будет унаследован другими конкретными классами (Apple, Orange), каждый из которых должен предоставлять стандартный фабричный метод CreateInstance () для создания и инициализации экземпляра.
Если бы статическое наследование членов было возможным, я бы поместил фабричный метод в базовый класс и использовал бы вызов виртуального метода к производному классу, чтобы получить тип, из которого должен быть инициализирован конкретный экземпляр. Клиентский код просто вызовет Apple.CreateInstance () для получения полностью инициализированного экземпляра Apple.
Но очевидно, что это невозможно, поэтому может кто-нибудь объяснить, как мой дизайн должен измениться, чтобы обеспечить ту же функциональность.
источник
Ответы:
Одна идея:
public abstract class Fruit<T> where T : Fruit<T>, new() { public static T CreateInstance() { T newFruit = new T(); newFruit.Initialize(); // Calls Apple.Initialize return newFruit; } protected abstract void Initialize(); } public class Apple : Fruit<Apple> { protected override void Initialize() { ... } }
И назовите так:
Никаких дополнительных фабричных классов не требуется.
источник
Apple
конструктор менее общедоступным, посколькуwhere T : Fruit<T>, new()
требуетT
наличия общедоступного конструктора. @Matt Hamsmith - ваш код не компилируется, пока вы его не удалитеprotected Apple() { }
. Я уже тестировал это в VS.Переместите фабричный метод из типа и поместите его в собственный класс Factory.
public abstract class Fruit { protected Fruit() {} public abstract string Define(); } public class Apple : Fruit { public Apple() {} public override string Define() { return "Apple"; } } public class Orange : Fruit { public Orange() {} public override string Define() { return "Orange"; } } public static class FruitFactory<T> { public static T CreateFruit<T>() where T : Fruit, new() { return new T(); } }
Но, поскольку я смотрю на это, нет необходимости перемещать метод Create в его собственный класс Factory (хотя я думаю, что это предпочтительнее - разделение проблем -), вы можете поместить его в класс Fruit:
public abstract class Fruit { public abstract string Define(); public static T CreateFruit<T>() where T : Fruit, new() { return new T(); } }
И, чтобы увидеть, работает ли это:
class Program { static void Main( string[] args ) { Console.WriteLine (Fruit.CreateFruit<Apple> ().Define ()); Console.WriteLine (Fruit.CreateFruit<Orange> ().Define ()); Console.ReadLine (); } }
источник
Почему бы не создать фабричный класс (шаблонный) с помощью метода create?
источник
Я бы сделал что-то подобное
public abstract class Fruit() { public abstract void Initialize(); } public class Apple() : Fruit { public override void Initialize() { } } public class FruitFactory<T> where T : Fruit, new { public static <T> CreateInstance<T>() { T fruit = new T(); fruit.Initialize(); return fruit; } } var fruit = FruitFactory<Apple>.CreateInstance()
источник
WebRequest
Класс и его производные типы в .NET BCL представляют собой хороший пример того , как такого рода конструкции могут быть выполнены относительно хорошо.У
WebRequest
класса есть несколько подклассов, включаяHttpWebRequest
иFtpWebReuest
. Теперь этотWebRequest
базовый класс также является фабричным типом и предоставляет статическийCreate
метод (конструкторы экземпляров скрыты, как того требует фабричный шаблон).public static WebRequest Create(string requestUriString) public static WebRequest Create(Uri requestUri)
Этот
Create
метод возвращает конкретную реализациюWebRequest
класса и использует URI (или строку URI) для определения типа создаваемого и возвращаемого объекта.Конечным результатом этого является следующий шаблон использования:
var httpRequest = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com/"); // or equivalently var httpRequest = (HttpWebRequest)HttpWebWebRequest.Create("http://stackoverflow.com/"); var ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://stackoverflow.com/"); // or equivalently var ftpRequest = (FtpWebRequest)FtpWebWebRequest.Create("ftp://stackoverflow.com/");
Я лично считаю, что это хороший подход к решению проблемы, и действительно, похоже, что это предпочтительный метод создателей .NET Framework.
источник
Прежде всего, отсутствие статических инициализаторов, которые могут быть виртуальными, не означает, что у вас не может быть «стандартных» методов-членов, которые могут быть перегружены. Во-вторых, вы можете вызывать свои виртуальные методы из конструкторов, и они будут работать должным образом, так что здесь нет никаких проблем. В-третьих, вы можете использовать дженерики для создания типобезопасной фабрики.
Вот код, который использует метод factory + member Initialize (), который вызывается конструктором (и он защищен, поэтому вам не нужно беспокоиться, что кто-то вызовет его снова после создания объекта):
abstract class Fruit { public Fruit() { Initialize(); } protected virtual void Initialize() { Console.WriteLine("Fruit.Initialize"); } } class Apple : Fruit { public Apple() : base() { } protected override void Initialize() { base.Initialize(); Console.WriteLine("Apple.Initialize"); } public override string ToString() { return "Apple"; } } class Orange : Fruit { public Orange() : base() { } protected override void Initialize() { base.Initialize(); Console.WriteLine("Orange.Initialize"); } public override string ToString() { return "Orange"; } } class FruitFactory { public static T CreateFruit<T>() where T : Fruit, new() { return new T(); } } public class Program { static void Main() { Apple apple = FruitFactory.CreateFruit<Apple>(); Console.WriteLine(apple.ToString()); Orange orange = new Orange(); Console.WriteLine(orange.ToString()); Fruit appleFruit = FruitFactory.CreateFruit<Apple>(); Console.WriteLine(appleFruit.ToString()); } }
источник
Я бы сказал, что лучше всего создать виртуальный / абстрактный метод Initialise для класса фруктов, который должен быть вызван, а затем создать внешний класс `` фабрика фруктов '' для создания экземпляров:
public class Fruit { //other members... public abstract void Initialise(); } public class FruitFactory() { public Fruit CreateInstance() { Fruit f = //decide which fruit to create f.Initialise(); return f; } }
источник