2013-08-19 51 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); 
} 
相關問題