2016-03-21 5 views
2

Ich versuche, eine Klassenstruktur in WPF/MVVM/Entity Framework zu erstellen, die so allgemein wie möglich ist. Ich verwende Prisma & Einheit. Wirklich, obwohl ich denke, dass nichts davon zählt, es sei denn irgendwie kann ich Prism/Unity in der Lösung verwenden.In C# kann ich eine spezifische Implementierung eines Generik basierend auf dem Typ (T) verwenden

Ich habe ein System, das eingerichtet ist und perfekt funktioniert, bis ich Code ausführen muss, der für den Typ (T) spezifisch ist.

Was ich wirklich möchte ist irgendwie das System verwendet die nicht-generische Version einer Klasse, wenn Typ T hat eine abgeleitete Klasse, die übereinstimmt, sonst möchte ich es die generische Version verwenden.

In einem Super-Beispiel verwässerte ich eine Basisklasse haben:

public class BaseClass<T> 
{ 
    public T MyObject; 

    protected virtual int GetErrorCount() 
    { 
     int errorCount = 0; 

     if (MyObject == null) 
      errorCount++; 

     return errorCount; 
    } 
} 

Und eine Unterklasse:

public class StringClass : BaseClass<String> 
{ 
    protected override int GetErrorCount() 
    { 
     int errorCount = base.GetErrorCount(); 

     if (MyObject != null && MyObject.Length < 5) 
      errorCount++; 

     return errorCount; 
    } 
} 

und eine Implementierungsklasse:

public class ImplementationClass<T> 
{ 
    public BaseClass<T> MyBaseObject; 

    public ImplementationClass() 
    { 
     // If T is a string I want to use StringClass instead of the base 
     // And it should happen automagically 
     MyBaseObject = new BaseClass<T>(); 
    } 
} 

ich ein paar haben Ideen für die Umgehung von Schaltern oder das Umwerfen der Logik (von Daten für die Logik) auf die POCO-Klasse bu Das will ich nicht machen.

Ich habe die Idee, ein Wörterbuch und einige Nachschlageklassen zu verwenden, die verwendet werden, wann immer ich den eigentlichen Typ auflösen muss - aber wirklich das, was Prism tut, also würde ich denken, dass ich das irgendwie benutzen könnte ... kann es nicht herausfinden.

Irgendwelche Ideen, wie ich das so generisch wie möglich erreichen kann?


Haben Sie es herausgefunden, irgendwelche Rückmeldungen dazu? Auf was muss ich achten?

Solution (ermöglicht MyBaseObject auch geändert werden):

public static class HelperExt 
{ 
    internal static IEnumerable<Type> GetInhiritingTypes(this Type fatherT) 
    { 
     return fatherT.Assembly.GetTypes().Where(t => t.IsSubclassOf(fatherT) && !t.IsAbstract); 
    } 
} 

public class ImplementationClass<T> 
{ 
    private BaseClass<T> _myBaseObject; 
    public BaseClass<T> MyBaseObject 
    { 
     get { return _myBaseObject; } 
     set 
     { 
      Type instType = typeof(BaseDataViewModel<T>).GetInhiritingTypes().FirstOrDefault(t => t.BaseType.GetGenericArguments().Contains(typeof(T))); 
      if (instType != null) 
       SetProperty(ref _myBaseObject, (BaseClass<T>)Activator.CreateInstance(instType)); 
      else 
       SetProperty(ref _myBaseObject, value); 
     } 
    } 

    public ImplementationClass() 
    { 
     // If T is a string I want to use StringClass instead of the base 
     // And it should happen automagically 
     MyBaseObject = new BaseClass<T>(); 
    } 
} 
+0

ist es sinnvoll, Aggregation statt Vererbung zu verwenden? – BatteryBackupUnit

+0

Ich bin mir nicht sicher, wie ich hier aggregieren könnte, zumindest nicht bei der Lösung des Problems und ohne die generische Implementierung zu verlieren. –

+0

Ihre Verwendung von Generika scheint mir sinnlos. Sie können jedoch mehrere private Member deklarieren, einen für jeden Typ, den Sie verwenden möchten, und dann den, den Sie für geeignet halten, basierend auf dem Typ von T. –

Antwort

1

Sie diese mit einigen Überlegungen zu tun, erste Hilfsfunktion:

public static IEnumerable<Type> GetInhiritingTypes(this Type fatherT) 
    { 
     return fatherT.Assembly.GetTypes().Where(t => t.IsSubclassOf(fatherT) && !t.IsAbstract); 
    } 

Dann:

Type genericType = typeof(string); 
Type typeToInstantiate = typeof(BaseClass<String>).GetInheritingTypes().FirstOrDefault(t=>t.GetGenericsArguments().Contains(genericType)); 
if (typeToInstantiate != null) 
    MyBaseObject = (BaseClass<T>)Activator.CreateInstance(typeToInstantiate); 
else 
    MyBaseObject = new BaseClass<T>(); 

Aber es gibt sehr wenig "Sicherheit" in der Verwendung einer solchen Methode. Sie können auch eine Factory-Funktion schreiben, die einen Klassennamen annimmt und eine Instanz dieser Klasse instanziiert.

+0

Ich versuche, dies zur Arbeit zu bringen. Ich habe es in kleinere Schritte aufgeteilt. Ich kann sehen, dass 'typeToInstantiate' den richtigen Elterntyp hat (d. H.' StringClass'), aber 'GetGenericArguments()' gibt ein leeres Array zurück. Irgendwelche Ideen. –

+0

Eh ... Ich habe es herausgefunden. Sieht aus, als müsste ich 'instTypes.FirstOrDefault (t => t.BaseType.GetGenericArguments(). Enthält (genType)) <<- der Schlüssel ist BaseType. –

Verwandte Themen