2017-04-13 7 views
1

Ich beginne mit Generika-Typen und ich bin mit meinem Projekt fest. Vielleicht habe ich Generika nicht sehr gut verstanden. Erklärungen wurden inline eingefügt. Im Grunde brauche ich den Do() -Methode zu implementieren, aber ich weiß nicht, wie < T2 zu lösen >:Casting generische Typen zu Unterklasse

public abstract class MyGenericClass<T> { } 

    public class MyGenericClass<T, T2> : MyGenericClass<T> 
    { 
     public Expression<Func<T, T2>> expression; 

     public MyGenericClass(Expression<Func<T, T2>> expression) 
     { 
      this.expression = expression; 
     } 
    } 

    public class MyClass<T> 
    { 
     // I need to mantain a list of my generic class for later use. 
     // I don't know T2 at this point. 
     // So I Chose to use Inheritance as a workaround (MyGenericClass<T> and MyGenericClass<T, T2>). 
     // Maybe it's not a good solution but I counldn't find out other solution. 
     public List<MyGenericClass<T>> MyGenericList = new List<MyGenericClass<T>>(); 

     // I receive the parametric argument T2 here as part of an Expresion. 
     // And I keep the expression in my list. 
     public MyGenericClass<T, T2> ReceivingMethod<T2>(Expression<Func<T, T2>> expression) 
     { 
      MyGenericClass<T, T2> genericImp = new MyGenericClass<T, T2>(expression); 
      MyGenericList.Add(genericImp); 
      return genericImp; 
     } 
    } 

    public class Client<T> 
    { 
     MyClass<T> class1; 

     // class1 has been created and his field MyGenericList has beed populated. 
     // Then when I call Do().... 
     public void Do() 
     { 
      foreach (var item in class1.MyGenericList) 
      { 
       // ...I need something like this here, 
       // but it does not compile because I don't know T2 here. 
       // The caller of Do() method doesn't know T2. 
       MyGenericClass<T, T2> myGenericItem = (MyGenericClass<T, T2>)item; 
       var a = myGenericItem.expression; 
      } 

     } 
    } 
+1

Haben Sie darüber nachgedacht statt verwenden? –

+1

Sie müssen an dieser Stelle Reflexion verwenden. Oder versuchen Sie wirklich, ein anderes Design herauszufinden. Mit Generics können Sie Code schreiben, der weitgehend unabhängig vom Typ ist, damit Sie nicht viele verschiedene Typen zusammen gruppieren können. – juharr

+1

Wie wird 'Do'' 'MyGenericItem' verwenden? Wenn es nichts Spezifisches für "T2" zu tun braucht, dann können Sie abstrakte Methoden für 'MyGenericClass ' definieren, das in der abgeleiteten Klasse implementiert ist und von 'Do' aufgerufen wird. – KMoussa

Antwort

0

Lösung 1 Inspiriert von @KMoussa Kommentar zu sein. Ich habe die Verantwortung für MyGenericClass mit einer abstrakten Methode delegiert. Dies scheint ein besseres Design zu sein. Alle Unterklassen implementieren diese Methode (DoTheWork()). Und aus meiner Client.Do() -Methode mit nur T param aufgerufen werden:

public abstract class MyGenericClass<T> 
    { 
     public abstract string DoTheWork(); 
    } 

    public class MyGenericClass<T, T2> : MyGenericClass<T> 
    { 
     public override string DoTheWork() 
     { 
      // .... I can use expression here 
     } 

     private Expression<Func<T, T2>> expression { get; set; } 

     public MyGenericClass(Expression<Func<T, T2>> expression) 
     { 
      this.expression = expression; 
     } 
    } 

    public class MyClass<T> 
    { 
     public List<MyGenericClass<T>> MyGenericList = new List<MyGenericClass<T>>(); 
     public void ReceivingMethod<T2>(Expression<Func<T, T2>> expression) 
     { 
      MyGenericClass<T, T2> genericImp = new MyGenericClass<T, T2>(expression); 
     } 
    } 

    public class Client<T> 
    { 
     MyClass<T> class1; 
     public void Do() 
     { 
      // I don't need to cast to MyGenericClass<T, T2> 
      foreach (MyGenericClass<T> myGenericItem in class1.MyGenericList) 
      { 
       string result = myGenericItem.DoTheWork(); 
      } 

     } 
    } 

Lösung 2 Inspiriert von @ ja72 und @juharr Kommentaren. Reflexion verwenden. Zuerst speichere ich den Typ T2 in MyGenericClass mit einer abstrakten Eigenschaft. Dann kann ich eine generische Methode (MethodWithArgument) aufrufen Reflexion verwendet, so kann ich die Parameter für das Casting vorstellen:

public abstract class MyGenericClass<T> 
{ 
    public abstract Type type { get; set; } 
} 

public class MyGenericClass<T, T2> : MyGenericClass<T> 
{ 
    public Expression<Func<T, T2>> expression { get; set; } 

    public MyGenericClass(Expression<Func<T, T2>> expression) 
    { 
     type = typeof(T2); // I save the type of T2 
     this.expression = expression; 
    } 
} 

public class MyClass<T> 
{ 
    public List<MyGenericClass<T>> MyGenericList = new List<MyGenericClass<T>>(); 
    public void ReceivingMethod<T2>(Expression<Func<T, T2>> expression) 
    { 
     MyGenericClass<T, T2> genericImp = new MyGenericClass<T, T2>(expression); 
    } 
} 

public class Client<T> 
{ 
    MyClass<T> class1; 
    public void Do() 
    { 
     foreach (MyGenericClass<T> myGenericItem in class1.MyGenericList) 
     { 
      MethodInfo method = GetType().GetMethod("MethodWithArgument"); 
      MethodInfo generic = method.MakeGenericMethod(new Type[] { myGenericItem.type }); 
      string g = (string)generic.Invoke(this, new object[] { myGenericItem }); 
     } 
    } 

    // I introduce T2 in this method 
    public string MethodWithArgument<T2>(MyGenericClass<T> myClass) 
    { 
     // Now, the casting is valid 
     MyGenericClass<T, T2> mySubClass = (MyGenericClass<T, T2>)myClass; 
     var a = mySubClass.expression; 
     // ... I can work with expression here 
    } 
} 
1

Sie haben Do() die T2 Parameter irgendwie zu geben. Meine Lösung besteht also darin, einen Methodenparameter desselben Typs zu erstellen. Ich verschachtelte auch die Typen, um sicherzustellen, dass sie alle auf denselben T verweisen.

I umbenannt die Parameter auch anschaulichere

// T -> TArg 
// T2 -> TResult 
public abstract class MyBaseClass<TArg> 
{ 
    public class MyExpressionClass<TResult> : MyBaseClass<TArg> 
    { 
     public Expression<Func<TArg, TResult>> Expression { get; private set; } 
     public MyExpressionClass(Expression<Func<TArg, TResult>> expression) 
     { 
      this.Expression=expression; 
     } 
    } 

    public class MyCollectionClass 
    { 
     public List<MyBaseClass<TArg>> MyGenericList = new List<MyBaseClass<TArg>>(); 

     public MyExpressionClass<TResult> ReceivingMethod<TResult>(Expression<Func<TArg, TResult>> expression) 
     { 
      var genericImp = new MyExpressionClass<TResult>(expression); 
      MyGenericList.Add(genericImp); 
      return genericImp; 
     } 
    } 

    public class Client 
    { 
     public MyCollectionClass List = new MyCollectionClass(); 

     public void Do<TResult>() 
     { 
      foreach(var item in List.MyGenericList) 
      { 
       var expr = item as MyExpressionClass<TResult>; 
       if(expr!=null) 
       { 
        var a = expr.Expression; 
        Console.WriteLine(a); 
       } 
      } 
     } 
    } 
} 



class Program 
{ 
    static void Main(string[] args) 
    { 
     var client = new MyBaseClass<int>.Client(); 
     // add conversion expressions 
     client.List.ReceivingMethod((i) => (i).ToString()); 
     client.List.ReceivingMethod((i) => (2*i).ToString()); 
     client.List.ReceivingMethod((i) => (3*i).ToString()); 

     // The programmer has to manually enforce the `string` type 
     // below based on the results of the expressions above. There 
     // is no way to enforce consistency because `TResult` can be 
     // _any_ type. 
     client.Do<string>(); 

     // Produces the following output 
     // 
     // i => i.ToString() 
     // i => (2*i).ToString() 
     // i => (3*i).ToString() 
    } 
} 
+0

Ich kann 'MyExpressionClass ...' nicht kompilieren. Ich muss den Typparameter 'TArg' hinzufügen:' MyExpressionClass ... 'zum Kompilieren.Das Problem ist, als der Aufrufer von 'Do()' nicht weiß '' – Jesus

+0

Der Code in meinem Beispiel kompiliert 100%. Ich habe es vor dem Posten überprüft. Wenn Sie einen Fehler erhalten, könnte es irgendwo einen Tippfehler geben. 'MyExpression <>' ist verschachtelt _inside_ 'MyBaseClass <>' und es verwendet 'TArg' daraus. – ja72

+0

Ja, kompiliert 100%. Ich habe nicht gesehen, dass die Klassen verschachtelt waren. Es tut uns leid!! – Jesus