Is it possible to create a derived class from the constructor of the base class?

I speak 3 classes, animals, cats and dogs.

// calling code var x = new Animal("Rex"); // would like this to return a dog type var x = new Animal("Mittens"); // would like this to return a cat type if(x.GetType() == typeof(Dog)) { x.Bark(); } else { x.Meow(); } class Animal { public Animal(string name) { // check against some list of dog names ... find rex // return Animal of type Dog. // if not... // check against some list of cat names ... find mittens // return Animal of type Cat. } } 

Is it possible somehow? If not, can I do something similar?

+6
source share
4 answers

What you are looking for is either a "virtual constructor" (not possible in C #) or a Factory pattern.

 class Animal { // Factory method public static Animal Create(string name) { Animal animal = null; ... // some logic based on 'name' animal = new Zebra(); return animal; } } 

The Factory method can also be placed in another class (Factory). This gives a better outcome, etc.

+9
source

No. Basically the correct fix is ​​to use a static method that can create an instance of the correct type:

 var x = Animal.ForName("Rex"); var x = Animal.ForName("Mittens"); ... public abstract class Animal { public static Animal ForName(string name) { if (dogNames.Contains(name)) { return new Dog(name); } else { return new Cat(name); } } } 

Or it could be an instance method in type AnimalFactory (or something else). This would be a more extensible approach - a factory could implement an interface, for example, and could be introduced into the class that was supposed to instantiate. It really depends on the context, though - sometimes this approach is redundant.

Basically, calling new Foo(...) always instantiates exactly Foo . If a static method declared with a return type of Foo can return a reference to any type compatible with Foo .

+5
source

No, I don’t think it is possible the way you want.

You can create a static class that has a method that returns an animal based on the name, for example.

 static Animal CreateAnimal(string name) { if(catList.Contains(name)) return new Cat(name"); else if(dogList.Contains(name)) return new Dog(name); return null; } 
+1
source

Other answers show that you need to use the factory pattern, but I would like to give you a more β€œpractical” example of how you do this. I did exactly what you did, however I worked with the EPL2 printer language . When I saw X , I needed to create an instance of the Rectangle class, when I saw A I needed to create an instance of the Text class.

(I wrote this a long time ago , so I'm sure some of my things can be improved).

 public partial class Epl2CommandFactory { #region Singelton pattern private static volatile Epl2CommandFactory m_instance; private static object m_syncRoot = new object(); public static Epl2CommandFactory Instance { get { if (m_instance == null) { lock (m_syncRoot) { if (m_instance == null) { m_instance = new Epl2CommandFactory(); } } } return m_instance; } } #endregion #region Constructor private Epl2CommandFactory() { m_generalCommands = new Dictionary<string, Type>(); Initialize(); } #endregion #region Variables private Dictionary<string, Type> m_generalCommands; private Assembly m_asm; #endregion #region Helpers private void Initialize() { Assembly asm = Assembly.GetAssembly(GetType()); Type[] allTypes = asm.GetTypes(); foreach (Type type in allTypes) { // Only scan classes that are not abstract if (type.IsClass && !type.IsAbstract) { // If a class implements the IEpl2FactoryProduct interface, // which allows retrieval of the product class key... Type iEpl2FactoryProduct = type.GetInterface("IEpl2GeneralFactoryProduct"); if (iEpl2FactoryProduct != null) { // Create a temporary instance of that class... object inst = asm.CreateInstance(type.FullName); if (inst != null) { // And generate the product classes key IEpl2GeneralFactoryProduct keyDesc = (IEpl2GeneralFactoryProduct)inst; string key = keyDesc.GetFactoryKey(); m_generalCommands.Add(key, type); inst = null; } } } } m_asm = asm; } #endregion #region Methods public IEpl2Command CreateEpl2Command(string command) { if (command == null) throw new NullReferenceException("Invalid command supplied, must be " + "non-null."); Type type; if (!m_generalCommands.TryGetValue(command.Substring(0, 2), out type)) m_generalCommands.TryGetValue(command.Substring(0, 1), out type); if (type != default(Type)) { object inst = m_asm.CreateInstance(type.FullName, true, BindingFlags.CreateInstance, null, null, null, null); if (inst == null) throw new NullReferenceException("Null product instance. " + "Unable to create necessary product class."); IEpl2Command prod = (IEpl2Command)inst; prod.CommandString = command; return prod; } else { return null; } } #endregion } 

How the code works, I use a singleton pattern to create a factory class so that people can call var command = Epl2CommandFactory.Instance.CreateEpl2Command("...") ; passing in command line EPL2 and returning an instance of the class representing this particular class.

During initialization, I use reflection to search for classes that support the IEpl2GeneralFactoryProduct interface, if the class supports the interface, the factory stores one or two letter codes representing the printer command in the type dictionary.

When trying to create a command, the factory looks at the printer command in the dictionary and creates the correct class, and then passes the full command line to this class for further processing.

Here is a copy of the command class, and these are the parents if you want to see it

Rectangle :

 [XmlInclude(typeof(Rectangle))] public abstract partial class Epl2CommandBase { } /// <summary> /// Use this command to draw a box shape. /// </summary> public class Rectangle : DrawableItemBase, IEpl2GeneralFactoryProduct { #region Constructors public Rectangle() : base() { } public Rectangle(Point startingLocation, int horozontalEndPosition, int verticalEndPosition) : base(startingLocation) { HorizontalEndPosition = horozontalEndPosition; VerticalEndPosition = verticalEndPosition; } public Rectangle(int x, int y, int lineThickness, int horozontalEndPosition, int verticalEndPosition) : base(x, y) { LineThickness = lineThickness; HorizontalEndPosition = horozontalEndPosition; VerticalEndPosition = verticalEndPosition; } #endregion #region Properties [XmlIgnore] public int LineThickness { get; set; } [XmlIgnore] public int HorizontalEndPosition {get; set;} [XmlIgnore] public int VerticalEndPosition { get; set; } public override string CommandString { get { return String.Format("X{0},{1},{2},{3},{4}", X, Y, LineThickness, HorizontalEndPosition, VerticalEndPosition); } set { GenerateCommandFromText(value); } } #endregion #region Helpers private void GenerateCommandFromText(string command) { if (!command.StartsWith(GetFactoryKey())) throw new ArgumentException("Command must begin with " + GetFactoryKey()); string[] commands = command.Substring(1).Split(','); this.X = int.Parse(commands[0]); this.Y = int.Parse(commands[1]); this.LineThickness = int.Parse(commands[2]); this.HorizontalEndPosition = int.Parse(commands[3]); this.VerticalEndPosition = int.Parse(commands[4]); } #endregion #region Members public override void Paint(Graphics g, Image buffer) { using (Pen p = new Pen(Color.Black, LineThickness)) { g.DrawRectangle(p, new System.Drawing.Rectangle(X, Y, HorizontalEndPosition - X, VerticalEndPosition - Y)); } } public string GetFactoryKey() { return "X"; } #endregion } 

DrawableItemBase :

 public abstract class DrawableItemBase : Epl2CommandBase, IDrawableCommand { protected DrawableItemBase() { Location = new Point(); } protected DrawableItemBase(Point location) { Location = location; } protected DrawableItemBase(int x, int y) { Location = new Point(); X = x; Y = y; } private Point _Location; [XmlIgnore] public virtual Point Location { get { return _Location; } set { _Location = value; } } [XmlIgnore] public int X { get { return _Location.X; } set { _Location.X = value; } } [XmlIgnore] public int Y { get { return _Location.Y; } set { _Location.Y = value; } } abstract public void Paint(Graphics g, Image buffer); } 

Epl2CommandBase :

 public abstract partial class Epl2CommandBase : IEpl2Command { protected Epl2CommandBase() { } public virtual byte[] GenerateByteCommand() { return Encoding.ASCII.GetBytes(CommandString + '\n'); } public abstract string CommandString { get; set; } } 

Different interfaces:

 public interface IEpl2GeneralFactoryProduct { string GetFactoryKey(); } public interface IEpl2Command { string CommandString { get; set; } } public interface IDrawableCommand : IEpl2Command { void Paint(System.Drawing.Graphics g, System.Drawing.Image buffer); } 
0
source

Source: https://habr.com/ru/post/952033/


All Articles