2012-12-19 18 views
21

Mam klasy abstrakcyjnej z kilkoma klasie pochodnejGet klasę wartości ciągu

public abstract class MyObject 
{ 
    public string name { get; set; } 
    public bool IsObject(string pattern); 
    ... 
} 

public class MyObjectA : MyObject 
{ 
    public string name { get { return "MyObjectA"; } set; } 
    public bool IsObject(string pattern) { ... } 
    ... 
} 

public class MyObjectB: MyObject 
{ 
    public string name { get { return "MyObjectB"; } set; } 
    public bool IsObject(string pattern) { ... } 
    ... 
} 

teraz chcę mieć funkcję, która zwraca moją specyficzną klasę (MyObjectA/MyObectB) w oparciu o ciąg. Problem polega na tym, że mam wiele klauzul if/else na to:

public MyObject Create(string pattern) 
{ 
    MyObjectA obj = new MyObjectA(); 
    if(obj.IsObject(pattern) 
    { 
     return obj; 
    } 
    else 
    { 
     MyObjectB objb = new MyObjectB(); 
     if(objb.IsObject(pattern); 
      return objb; 
     else 
      ... 
    } 
} 

To wygląda po prostu okropnie. Jaki byłby lepszy sposób na zrobienie tego?

Odpowiedz

28

Tak, użyj Odbicia.

Można użyć Type.GetType uzyskać instancję Type dla klasy sznurkiem, a następnie oznacz ją przy użyciu Activator.CreateInstance, coś takiego:

public MyObject Create(string pattern) 
{ 
    Type t = Type.GetType(pattern); 
    if (t == null) { 
     throw new Exception("Type " + pattern + " not found."); 
    } 
    return Activator.CreateInstance(t); 
} 

można użyć Activator.CreateInstance(string, string) przeciążenie także, ale to nie będzie bezpośrednio zwróć nową instancję wymaganego Type.

5

Jak powiedział Rudi Visser, powinieneś użyć reflection.

Również aby uzyskać nazwę klasy, nie powinieneś jej wpisywać na stałe. Jeśli whant korzystać z właściwości name wystarczy napisać

public abstract class MyObject 
{ 
    public string name 
    { 
     get 
     { 
      return this.GetType().Name; 
     } 
    } 
    public bool IsObject(string pattern); 
    ... 
} 

jeśli nie masz nazwy klasy, tylko jakiś ciąg znaków, który go reprezentuje, niż można sprawdzić wszystkie klasy pochodzące od MyObject

public MyObject Create(string pattern) 
{ 
    Type[] types = Assembly.GetExecutingAssembly().GetTypes(); 
    foreach (Type type in types.Where(t => t.IsSubclassOf(typeof(MyObject)))) 
    { 
     MyObject obj = (MyObject)Activator.CreateInstance(type); 
     if (obj.name == pattern) 
     { 
      return obj; 
     } 
    } 
    throw new Exception("Type " + pattern + " not found."); 
} 
+0

+1 Dobre połączenie. Jawne usunięcie 'set' również jest dobre. –

+0

To był tylko przykład. Nazwa-string nie jest dokładną nazwą klasy. Mogę ją jednak przekształcić. – Link

0

można to zrobić przy użyciu odbicia takiego ...

public object getInstance(string assemblyName, string className, object[] constructorParameters) 
    { 
     System.Reflection.Assembly asm = System.Reflection.Assembly.Load(assemblyName); 
     return asm.CreateInstance(className, false, System.Reflection.BindingFlags.CreateInstance, null, constructorParameters, null, null); 
    } 

AssemblyName - pełna ścieżka + montaż wymienić

className - pełna nazwa klasy

1

Istnieje wiele dobrych odpowiedzi na zadeklarowane pytanie. Chciałbym jednak zaproponować, aby użyć wzoru fabrycznego dla tego rodzaju wymagań.

Twoja "fabryka" może być tak prosta, jak dodanie statycznej metody GetNewMyObject(string pattern) do twojej klasy bazowej, a także protected static Dictionary<string, Type>. Wtedy twoje klasy pochodne mogą po prostu dodać swój wzorzec + typ do fabryki (w statycznym konstruktorze), umożliwiając dodanie nowych klas pochodnych do fabryki bez modyfikowania klasy bazowej.

ten sposób struny „wzór” nie musi być zgodna z nazwą typu i również nie trzeba robić żadnych logiczne pasujące do wzorca, jak można napisać:

public static MyObject GetNewMyObject(string pattern) 
{ 
    return (MyObject)Activator.CreateInstance(StaticTypeDictionary[pattern]); 
} 
Powiązane problemy