2016-05-11 14 views
-2

Ich möchte verstehen, warum der Code unten nicht funktioniert und wie man es beheben kann.C# Generika Methoden und Liste

wenn T doppelt oder lang ist Warum T nicht akzeptiert, doppelt oder lang zu werfen?

private IList<T> GetGenericList<T>(bool isDouble) where T : double, long 
    { 
     IList<T> Result = new List<T>(); 

     if (isDouble) 
     { 
      T Value = 2.5; // does not compile 
      Result.Add(Value); 
     } 
     else 
     { 
      T Value = 2; // does not compile 
      Result.Add(Value); 
     } 

     return Result; 
    } 

[UPDATE] Ok Leute nicht nervös sein! Lass mich verstehen, was du gesagt hast! :)

Was ist das Prinzip der Generika in C#?

Wenn ich de-Code in das folgende Beispiel ändern? (Get out of the box, es ist nur ein Beispiel, ignorieren Sie de Regeln (if (isDouble), was auch immer)

private IList<T> GetGenericList<T>(//whatever you want of param... 
) where T : double 
{ 
    IList<T> Result = new List<T>(); 
    T Value = 2.5; // does not compile 
    Result.Add(Value); 
    return Result; 
} 

Dank

+2

Es gibt keinen Grund, warum Sie isDouble als arg bereitstellen müssten; Der Typ von T kann zur Laufzeit bestimmt werden: 'Type typeParameterType = typeof (T);' –

+5

': double, long' bedeutet double * und * long - d. h. es ist unsinnig. – Blorgbeard

+0

Die generischen Einschränkungen sind UND, nicht ODER. Also T in Ihrem Fall muss sowohl doppelt als auch lang sein, was unwahrscheinlich erscheint ..! – Chris

Antwort

1

Es gibt drei Grund, warum Ihr Code wird nicht funktionieren..

  1. Ihre generische Einschränkung erfordert, dass die Art sowohl erbt von double und long die
  2. nicht möglich ist
  3. double und long können nicht als generische Einschränkungen verwendet werden, weil sie verschlossen sind.
  4. Es gibt keine implizite Konvertierung von double an einen unbekannten Typ

Es gibt kein „fix“ für die ersten beiden. Das Update für das letzte wäre object zuerst zu werfen:

if (isDouble) 
    { 
     T Value = (T)(object)2.5; // does not compile 
     Result.Add(Value); 
    } 
    else 
    { 
     T Value = (T)(object)2; // does not compile 
     Result.Add(Value); 
    } 

Es gibt keine Möglichkeit, die Methode zu bekommen genau so, wie Sie es wollen arbeiten, weil es keine Möglichkeit gibt T zu beschränken entweder double zu sein oder long. Sie könnte inspizieren den Typ mit Reflektion und eine Ausnahme auslösen, wenn es nicht einer von diesen ist, aber es gibt keine Möglichkeit, das zum Zeitpunkt der Kompilierung erzwingen.

Das Kernproblem ist, dass Ihr Code nicht generisch ist. Wahrer generischer Code interessiert nicht, was T ist (modulo einige sehr breite Parameter, wie es einige Schnittstelle implementieren muss). Die Anrufer bekommt zu bestimmen, was T ist, nicht die Methode, so dass alles, was unterschiedliche Logik basierend auf der tatsächlichen Art von T ist in der Regel ein Code Geruch. Dies scheint nur eine Lernübung zu sein, aber wenn Sie diese Funktion im wirklichen Leben benötigt würde ich empfehlen, zwei verschiedene Methoden mit:

private IList<double> GetDoubleList() 
{ 
    IList<double> Result = new List<double>(); 

    double Value = 2.5; 
    Result.Add(Value); 

    return Result; 
} 
private IList<long> GetLongList() 
{ 
    IList<long> Result = new List<long>(); 

    long Value = 2; 
    Result.Add(Value); 

    return Result; 
} 

Es ist nicht , die viel mehr als das, was Sie begann mit, es ist ein bisschen von der Wiederholung, aber nicht viel, es ist völlig typsicher, und es gibt keine Art Ambiguität innerhalb der Methoden.

Als nächstes können Sie den redundanten Code in eine generische Methode Refactoring (in diesem Fall brauchen Sie es nicht zu beschränken, da Sie die Typen aus dem Rufmethode steuern):

private IList<T> GetList<T>(T Value) 
{ 
    IList<T> Result = new List<T>(); 

    Result.Add(Value); 

    return Result; 
} 
private IList<double> GetDoubleList() 
{ 
    return GetList<double>(2.5); 
} 
private IList<long> GetLongList() 
{ 
    return GetList<long>(2); 
} 
+0

Die wirklich seltsame Sache ist, dass ich eine InvalidCastException mit 'T Value = (T) (object) 2;'. Die Art, wie ich es geschafft habe, war mit 'T Value = (T) (Objekt) 2L;' –

+0

Das liegt daran, dass der Typ _actual_ ein 'int' ist, da es sich um ein Integer-Literal handelt. Da Referenz-Umwandlungen erfordern, dass der _actual_ Typ kompatibel (anstatt konvertierbar) ist, wird die Umwandlung zur Laufzeit fehlschlagen. –

+0

Das habe ich mir gedacht, als ich es später inspiziert habe. –

0

isDouble Wert zur Laufzeit angegeben. Beide Aufgaben müssen sinnvoll zur Compile-Zeit unabhängig von Verzweigung

Sie versuchen, etwas mit Generika zu tun, die im Prinzip nicht getan werden können

0

Ihre Antwort zu beantworten siehe: Introduction to Generics (C# Programming Guide)

soweit Ihr aktuelles Problem, wo man nur double oder long will behandelt werden, Sie tun können, als D Stanley vorgeschlagen und nur etwas mit den anderen Typen. Hier ein Beispiel:

private IList<T> GetGenericList<T>() 
    { 
    Type parmType = typeof(T); 
    if (!parmType.Equals(typeof(double)) && !parmType.Equals(typeof(long))) 
    { 
     return null; 
    } 
    else 
    { 
     IList<T> Result = new List<T>(); 

     if (parmType.Equals(typeof(double))) 
     { 
      T Value = (T)(object)2.5; 
      Result.Add(Value); 
     } 
     else 
     { 
      T Value = (T)(object)2L; 
      Result.Add(Value); 
     } 

     return Result; 
    } 
    } 

Nun, wenn ich es wie folgt verwenden:

 List<double> lDbl = (List<double>)GetGenericList<double>(); 
    List<long> lLng = (List<long>)GetGenericList<long>(); 
    List<string> lStr = (List<string>)GetGenericList<string>(); 

ich am Ende mit den erwarteten Werten in lDbl und lLng, einschließlich der korrekten Datentypen. lStr ist jedoch auf null eingestellt, weil ich damit andere Handlingtypen als double oder long habe. Sie könnten genauso gut eine Ausnahme dort hineinwerfen.

+0

Danke Chris und alle! –

+0

Was ist mit den Güssen? 'Liste lDbl = GetGenericList ();' sollte in Ordnung sein, oder? – Blorgbeard

+0

Es erforderte explizites Casting, da es 'IList' zurückgibt. Andernfalls wird ein impliziter Casting-Fehler angezeigt. Die tatsächliche Nutzung kann variieren. Das war nur mein schneller Hack. –