multiple - c# type parameter




Übergibt explizit generische Typparameter an jede Schnittstelle (2)

In Generics FAQ: Best Practices sagt:

Mit dem Compiler können Sie generische Typparameter explizit auf jede Schnittstelle, aber nicht auf eine Klasse umwandeln:

interface ISomeInterface
{...}
class SomeClass
{...}
class MyClass<T> 
{
   void SomeMethod(T t)
   {
      ISomeInterface obj1 = (ISomeInterface)t;//Compiles
      SomeClass      obj2 = (SomeClass)t;     //Does not compile
   }
}

Ich sehe Einschränkungen sowohl für Klassen als auch für Schnittstellen, sofern die Klasse / Schnittstelle nicht als Einschränkungstyp angegeben ist.

Warum also solches Verhalten, warum ist es für Schnittstellen erlaubt?


Ich glaube, das liegt daran, dass die SomeClass in SomeClass abhängig von den verfügbaren Konvertierungen eine beliebige Anzahl von Dingen bedeuten kann, während die Umwandlung in ISomeInterface nur eine Referenzkonvertierung oder eine ISomeInterface kann.

Optionen:

  • Zuerst zum Objekt werfen:

    SomeClass obj2 = (SomeClass) (object) t;
    
  • Verwenden Sie stattdessen:

    SomeClass obj2 = t as SomeClass;
    

Offensichtlich müssen Sie im zweiten Fall auch einen Nichtigkeitscheck durchführen, wenn t kein SomeClass .

EDIT: Die Begründung hierfür ist in Abschnitt 6.2.7 der C # 4-Spezifikation gegeben:

Die obigen Regeln erlauben keine direkte explizite Konvertierung von einem unbeschränkten Typparameter in einen Nicht-Interface-Typ, was überraschend sein könnte. Der Grund für diese Regel besteht darin, Verwirrung zu vermeiden und die Semantik solcher Konvertierungen deutlich zu machen. Betrachten Sie zum Beispiel die folgende Deklaration:

class X<T>
{
    public static long F(T t) {
        return (long)t; // Error 
    }
} 

Wenn die direkte explizite Umwandlung von t in int zulässig wäre, könnte man leicht erwarten, dass X<int>.F(7) 7L zurückgeben würde. Dies würde jedoch nicht der Fall sein, da die numerischen Standardumrechnungen nur dann berücksichtigt werden, wenn bekannt ist, dass die Typen zur Bindungszeit numerisch sind. Um die Semantik zu verdeutlichen, muss stattdessen das obige Beispiel geschrieben werden:

class X<T>
{
    public static long F(T t) {
        return (long)(object)t; // Ok, but will only work when T is long
    }
}

Dieser Code kompiliert nun, führt aber X<int>.F(7) würde dann zur Laufzeit eine Ausnahme X<int>.F(7) , da ein boxed int nicht direkt in long konvertiert werden kann.


Im Vererbungs-Prinzip von C # könnten Interfaces mehrfach vererbt werden, aber nur einmal. Da die Vererbung von Interfaces eine komplexe Hierarchie hat, muss das .NET-Framework den generischen Typ T zum Zeitpunkt der Kompilierung nicht auf eine bestimmte Schnittstelle absichern. (EDIT) Im Gegenteil, eine Klasse könnte mit einer Typ-Constraint deklariert werden bei der Kompilierung als der folgende Code.

class MyClass<T> where T : SomeClass
{
   void SomeMethod(T t)
   {
      ISomeInterface obj1 = (ISomeInterface)t;
      SomeClass      obj2 = (SomeClass)t;     
   }
}




clr