2013-08-19 12 views
6

Mówię 3 klasy, zwierzę, kot & Pies.Czy można utworzyć klasę pochodną z konstruktora klasy podstawowej?

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

Czy to możliwe? Jeśli nie, jest coś podobnego, co mogę zrobić?

+2

Nie ograniczając się właściwie bez ... swoją bazę klasy musiałaby znać jego podklasy, które nie jest bardzo przydatna. –

Odpowiedz

9

To, czego szukasz, to "wirtualny konstruktor" (nie jest to możliwe w C#) lub wzór fabryczny.

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

     return animal; 
    } 
} 

Metoda Factory może być również umieszczona w innej klasie (Factory). To daje lepsze oddzielenie itp

5

nr Zasadniczo prawo fix jest użycie metody statycznej, które mogą utworzyć instancję prawym typu:

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

Albo może to być instancja metoda w sposób AnimalFactory typ (lub cokolwiek). To byłoby bardziej rozszerzalne podejście - na przykład fabryka mogłaby zaimplementować interfejs i mogłaby zostać wstrzyknięta do klasy, która potrzebowała stworzyć instancje. To naprawdę zależy od kontekstu - czasami takie podejście jest przesadne.

Zasadniczo, new Foo(...) wezwanie zawsze tworzy instancję dokładnieFoo. Podczas gdy metoda statyczna zadeklarowana z typem zwrotnym Foo może zwrócić odniesienie do dowolnego typu, który jest zgodny z Foo.

1

Nie Nie sądzę, że jest to możliwe w sposób, w jaki chcesz.

Można utworzyć klasę statyczną, która ma metodę zwracającą zwierzę na podstawie nazwy, np.

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

innych odpowiedzi pokazują, że trzeba użyć wzoru fabrycznego ale chciałem dać ci bardziej „praktyczne” Przykład w jaki sposób to zrobić. Zrobiłem dokładnie to, co robisz, ale pracowałem z EPL2 printer language. Kiedy zobaczyłem X, potrzebowałem stworzyć instancję klasy Rectangle, gdy zobaczyłem A, potrzebowałem stworzyć instancję klasy Text.

(Napisałem to: a long time ago, więc jestem pewien, że niektóre z rzeczy, które zrobiłem, mogą zostać ulepszone).

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 
} 

Sposób kod działa to używam singleton pattern stworzyć factory class więc ludzie mogą dzwonić var command = Epl2CommandFactory.Instance.CreateEpl2Command("..."); przekazywanie ciągu poleceń EPL2 i zwraca instancję klasy, która reprezentuje tę konkretną klasę.

Podczas inicjalizacji używam odbicia, aby znaleźć klasy obsługujące interfejs IEpl2GeneralFactoryProduct, jeśli klasa obsługuje interfejs, w którym fabrycznie zapisany jest jeden lub dwuliterowy kod reprezentujący polecenie drukarki w słowniku typów.

Podczas próby utworzenia polecenia fabryka wyszukuje polecenie drukarki w słowniku i tworzy prawidłową klasę, następnie przekazuje pełny ciąg poleceń do tej klasy w celu dalszego przetwarzania.

Oto kopia klasy polecenia i to rodzice, jeśli chciał, żeby go zobaczyć

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

różne interfejsy:

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); 
} 
Powiązane problemy