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

时间:2013-08-19 13:43:22

标签: c# inheritance polymorphism

我说过3个课程,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.
   }
}

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

4 个答案:

答案 0 :(得分:9)

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

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)类中。这样可以提供更好的去耦等。

答案 1 :(得分:5)

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

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兼容的任何类型的引用。

答案 2 :(得分:1)

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

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

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

    return null;
}

答案 3 :(得分:0)

其他答案显示您需要使用工厂模式,但我想为您提供一个更“实用”的示例,告诉您如何做到这一点。我完全按照你所做的去做,但我正在使用EPL2 printer language。当我看到X时,我需要创建一个类Rectangle的实例,当我看到A时,我需要创建一个类Text的实例。

(我写了a long time ago,所以我确信我所做的一些事情可以改进。)

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 pattern创建factory class,以便人们可以调用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);
}