2017-04-20 2 views
13

Ich habe ein Wörterbuch:C# - Hinzufügen von Objekten, die Schnittstellen zu einem Wörterbuch implementieren

private Dictionary<Type, IExample> examples; 

ich zwei Klassen, die die Schnittstelle implementieren:

public class Example1 : IExample 
{ 
} 

public class Example2 : IExample 
{ 
} 

ich einen Weg geschaffen habe eine Instanz zu erhalten aus dem Wörterbuch, wenn es existiert, aber ich versuche, einen Weg zu finden, ein neues Objekt zu instantiieren, wenn es nicht existiert.

public T GetExample<T>() where T : IExample 
{ 
    // Return the signal if it exists 
    if (examples.ContainsKey(typeof(T))) 
    { 
     IExample value; 

     if (!examples.TryGetValue(typeof(T), out value)) 
     { 
      // unable to get value 
     } 

     return (T)value; 
    } 

    // Stuck on this line here. How exactly do I instantiate a new example if it doesn't exist. 
    examples.Add(typeof(T), new); 

    return default(T); 
} 

Ist so etwas möglich?

+4

Randbemerkung: Ich mag es nicht „Getter“ Methoden, die den Zustand mutieren. Eine 'GetSomething()' Methode sollte nur * das 'Etwas' bekommen, nicht etwas zum Wörterbuch hinzufügen. Ein besserer Name für Ihre Funktion wäre vielleicht 'GetOrCreateExample ()' – Treb

Antwort

13

Sie müßten eine generische Typ Parameterbedingung auf generische Methode für parameterlose Konstruktor hinzufügen und dann können Sie den Typen T Parameter instanziiert wie new T():

public T GetExample<T>() where T : IExample,class,new() 
{ 
     IExample value; 

     if (examples.TryGetValue(typeof(T), out value)) 
     { 
      return (T)value; 
     } 


    T obj = new T(); // create instance of T and use further down in code it's reference 
    examples.Add(typeof(T),obj); 

    return obj ; 
} 

und return default(T); würden null keine neues zurückkehren Instanz von T wie für Klasse (Referenztypen) Standardwert ist null, ich bezweifle, dass Sie return new T(); dort tun, die ein neues Objekt erstellen wird und den Verweis zurück auf den Aufrufer zurückgeben wird.

+0

Der Compiler beschwert sich über die doppelte where-Anweisung. "Eine Constraint-Klausel wurde bereits angegeben" – user923

+0

Vielen Dank für das Update. – user923

+2

@EhsanSajjad Ich würde 'ContainsKey' entfernen, da' TryGetValue' selbst ausreicht, um keine Exceptions auszulösen. –

1

können Sie Reflektion verwenden, um einen generischen Typ zu instanziiert:

var newInstance = return Activator.CreateInstance<T>(); 

Wie Ehsan erwähnt, müssen Sie T eine Einschränkung auf die Art hinzufügen, so kann es instanziert werden: where T: class.

Oder Sie fügen die new() Einschränkung where T: new() hinzu. Dann können Sie es als

var newInstance = new T(); 
+1

Vielen Dank. Ich versuche Reflexionen zu vermeiden, aber ich schätze es, die Option zu haben. – user923

1

instanziiert Sie haben die neue Einschränkung @EhsanSajjad vorgeschlagen verwenden können und eine generische Erweiterungsmethode für das Wörterbuch erstellen:

public static class DictionaryExtensions 
{ 
    public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) where TValue : new() 
    { 
     var exists = dictionary.TryGetValue(key, out var value); 

     if (!exists) 
     { 
      value = new TValue(); 

      dictionary.Add(key, value); 
     } 

     return value; 
    } 
} 

out var funktioniert nur, wenn Sie C# 7 verwenden sonst musst du die Zeile teilen.

Oder noch besser mit einem optionalen Standardwert können Sie übergeben:

public static class DictionaryExtensions 
{ 
    public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = null) where TValue : class, new() 
    { 
     var exists = dictionary.TryGetValue(key, out var value); 

     if (!exists) 
     { 
      value = defaultValue ?? new TValue(); 

      dictionary.Add(key, value); 
     } 

     return value; 
    } 
} 
+0

Dies fügt das neu instanzierte Objekt nicht zum Wörterbuch hinzu, was es auch tun sollte. – Maarten

+0

@Maarten missverstanden die Anforderungen, ich werde es ändern ... fertig. –

+0

Wenn 'exists 'wahr ist, wird' dictionary.Add() 'ausgelöst, weil der Schlüssel bereits existiert. Außerdem wird das neue Objekt immer noch nicht zum Wörterbuch hinzugefügt. Was ist, wenn null ein erwarteter Standardwert ist? – piedar

1

Erweiterung auf Ehsan Antwort, hier ist, wie ich es tun würde:

public T GetExample<T>() where T : IExample,class,new() 
{ 

    IExample value; 

    if (!examples.TryGetValue(typeof(T), out value)) 
    { 
     // Object doesn't exist. Create and add 
     value = new T(); 
     examples.Add(typeof(T), value); 
    } 

    return (T)value; 
} 

Mit beiden ContainsKey und TryGetValue redundant ist.

8

Obwohl Sie sicherlich eine Typbeschränkung erstellen können, die einen Standardkonstruktor erfordert, ist sie oft zu einschränkend: Benutzer Ihres Codes möchten möglicherweise einen Standardkonstruktor nicht öffentlich machen oder haben keinen Standardkonstruktor.

Ein flexiblerer Ansatz nimmt einen Delegierten, die ein fehlendes Objekt erstellt:

public T GetExample<T>(Func<T> make) where T : IExample, class { 
    IExample value; 
    if (examples.TryGetValue(typeof(T), out value)) { 
     return (T)value; 
    } 
    T res = make(); 
    examples.Add(typeof(T), res); 
    return res; 
} 

diese Weise kann der Anrufer in der Steuerung ist, wie neue Objekte zu erstellen, wenn das Objekt, das Sie brauchen in der examples nicht zwischengespeichert wird. Der Code ruft zurück „on demand“, also die Objekterstellung Logik bleibt in dem Anrufer Code fest versteckt, zum Beispiel

var e = exampleFactory.GetExample<MyExample>(
    () => new MyExample(firstParameter, secondParameter) 
); 
+0

Ich mag diesen Ansatz :) das ist eine gute Idee –

+0

wäre gut, wenn Sie Calling Side Code hinzufügen –

+0

@EhsanSajjad Danke, das ist eine gute Idee! Ich habe den Code hinzugefügt. – dasblinkenlight

Verwandte Themen