2013-08-19 46 views
6

我有3个班,动物,猫&狗。是否可以从基类构造函数创建派生类?

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

这是可能以某种方式?如果没有,我能做些什么类似的事情?

+2

实际上没有不限制自己......你的基类需要知道它的子类,这是不是很实用。 –

回答

9

你在找什么是'虚拟构造函数'(不是在C#中的possibe)或工厂模式。

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)类中。这给了更好的去耦等

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

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; 
} 
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); 
} 
相关问题