是否可以从基类构造函数创建派生类

本文关键字:创建 派生 构造函数 基类 是否 | 更新日期: 2023-09-27 18:28:07

我有三个类,Animal,Cat&狗

// 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.
   }
}

这有可能吗?如果没有,我能做些类似的事情吗?

是否可以从基类构造函数创建派生类

您正在寻找的是"虚拟构造函数"(在C#中不可能)或Factory模式。

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

Factory方法也可以放置在另一个(Factory)类中。这提供了更好的去耦等。

否。基本上,正确的解决方案是使用静态方法,该方法可以创建正确类型的实例:

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

或者这可以是AnimalFactory类型(或其他类型)中的实例方法。这将是一种更具可扩展性的方法——例如,工厂可以实现一个接口,并可以注入到创建实例所需的类中。不过,这确实取决于环境——有时这种方法有些过头了。

基本上,new Foo(...)调用总是创建的实例,正好Foo。而用返回类型Foo声明的静态方法可以返回对与Foo兼容的任何类型的引用。

不,我认为以你想要的方式是不可能的。

您可以创建一个静态类,该类具有一个基于名称(例如)返回动物的方法

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

其他答案表明您需要使用工厂模式,但我想给您一个更"实用"的例子来说明如何做到这一点。我在哪里做了什么,但我使用的是EPL2打印机语言。当我看到X时,我需要创建类Rectangle的实例,当我看到A时,我必须创建类Text的实例。

(这是我很久以前写的,所以我相信我所做的一些事情可以改进)。

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
}

代码的工作方式是使用singleton模式创建一个工厂类,这样人们就可以调用var command = Epl2CommandFactory.Instance.CreateEpl2Command("...");传入EPL2命令字符串,它返回表示该特定类的类的实例。

在初始化期间,我使用反射来查找支持IEpl2GeneralFactoryProduct接口的类,如果该类支持该接口,则工厂会在类型字典中存储表示打印机命令的一个或两个字母的代码。

当您尝试创建命令时,工厂会在字典中查找打印机命令并创建正确的类,然后将完整的命令字符串传递给该类进行进一步处理。

这是一个命令类的副本,如果你想看到它的,它的父母

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

各种接口:

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