visual varias una studio sharp qué que polimorfismo metodos metodo herencia ejemplo constructores consola composicion codigo clases abstractos c# .net inheritance static

c# - varias - ¿Cuál es la alternativa correcta a la herencia de métodos estáticos?



visual studio c# herencia (7)

¿Por qué no crear una clase de fábrica (con plantilla) con un método de creación?

FruitFactory<Banana>.Create();

Entiendo que la herencia de métodos estáticos no es compatible con C #. También he leído varias discusiones (incluso aquí) en las que los desarrolladores afirman que es necesario contar con esta funcionalidad, a lo que la respuesta típica es "si necesita herencia de miembros estáticos, hay un defecto en su diseño".

De acuerdo, dado que OOP no quiere que siquiera piense en la herencia estática, debo concluir que mi aparente necesidad apunta a un error en mi diseño. Pero, estoy atascado. Realmente apreciaría algo de ayuda para resolver esto. Aquí está el desafío ...

Quiero crear una clase base abstracta (llamémosle Fruit) que encapsula un código de inicialización complejo. Este código no se puede colocar en el constructor, ya que parte de él se basará en llamadas a métodos virtuales.

La fruta será heredada por otras clases concretas (Apple, Orange), cada una de las cuales debe exponer un método de fábrica estándar CreateInstance () para crear e inicializar una instancia.

Si la herencia de miembros estáticos fuera factible, colocaría el método de fábrica en la clase base y usaría una llamada de método virtual a la clase derivada para obtener el tipo desde el cual se debe inicializar una instancia concreta. El código del cliente invocaría simplemente a Apple.CreateInstance () para obtener una instancia de Apple totalmente inicializada.

Pero claramente esto no es posible, por lo que alguien puede explicar cómo mi diseño debe cambiar para adaptarse a la misma funcionalidad.


Diría que lo mejor que se puede hacer es crear un método de inicialización virtual / abstracto en la clase de fruta que se debe llamar y luego crear una clase externa de ''fábrica de fruta'' para crear instancias:

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; } }


En primer lugar, el hecho de no tener inicializadores estáticos que puedan ser virtuales no significa que no pueda tener métodos de miembros "estándar", que podrían estar sobrecargados. En segundo lugar, puede llamar a sus métodos virtuales desde los constructores, y funcionarán como se esperaba, por lo que no hay problema aquí. En tercer lugar, puede usar genéricos para tener una fábrica segura para tipos.
Aquí hay un código que usa el método Factoryize + initialize () que es llamado por el constructor (y está protegido, por lo que no tiene que preocuparse de que alguien lo vuelva a llamar después de crear un objeto):

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()); } }


Haría algo como esto

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()


La clase WebRequest y sus tipos derivados en .NET BCL representan un buen ejemplo de cómo este tipo de diseño se puede implementar relativamente bien.

La clase WebRequest tiene varias subclases, incluidas HttpWebRequest y FtpWebReuest . Ahora, esta clase base de WebRequest es también un tipo de fábrica y expone un método de Create estático (los constructores de instancia están ocultos, según lo requerido por el patrón de fábrica).

public static WebRequest Create(string requestUriString) public static WebRequest Create(Uri requestUri)

Este método Create devuelve una implementación específica de la clase WebRequest y utiliza el URI (o cadena URI) para determinar el tipo de objeto para crear y devolver.

Esto tiene el resultado final del siguiente patrón de uso:

var httpRequest = (HttpWebRequest)WebRequest.Create("http://.com/"); // or equivalently var httpRequest = (HttpWebRequest)HttpWebWebRequest.Create("http://.com/"); var ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://.com/"); // or equivalently var ftpRequest = (FtpWebRequest)FtpWebWebRequest.Create("ftp://.com/");

Personalmente, creo que esta es una buena forma de abordar el problema, y ​​de hecho parece ser el método preferido de los creadores de .NET Framework.


Mueva el método de fábrica del tipo y colóquelo en su propia clase de fábrica.

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(); } }

Pero, como estoy viendo esto, no hay necesidad de mover el método Create a su propia clase Factory (aunque creo que es preferible -separación de inquietudes-), puedes ponerlo en la clase Fruit:

public abstract class Fruit { public abstract string Define(); public static T CreateFruit<T>() where T : Fruit, new() { return new T(); } }

Y, para ver si funciona:

class Program { static void Main( string[] args ) { Console.WriteLine (Fruit.CreateFruit<Apple> ().Define ()); Console.WriteLine (Fruit.CreateFruit<Orange> ().Define ()); Console.ReadLine (); } }


Una idea:

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() { ... } }

Y llame así:

Apple myAppleVar = Fruit<Apple>.CreateInstance();

No se necesitan clases de fábrica adicionales.