Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

explicitly cast generic type parameters to any interface

In Generics FAQ: Best Practices says :

The compiler will let you explicitly cast generic type parameters to any interface, but not to a class:

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

I see limitation reasonable for both, classes and interfaces, unless the class/interface is not specified as constraint type.

So why such behavior, why it is allowed for interfaces ?

like image 244
Incognito Avatar asked Jun 20 '11 05:06

Incognito


People also ask

Can a generic type be an interface?

Java Generic Classes and SubtypingWe can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses.

What is a generic type parameter?

Generic Methods A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.

What does the generic constraint of type interface do?

Interface Type Constraint You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter.


2 Answers

I believe this is because the cast to SomeClass can mean any number of things depending on what conversions are available, whereas the cast to ISomeInterface can only be a reference conversion or a boxing conversion.

Options:

  • Cast to object first:

      SomeClass obj2 = (SomeClass) (object) t; 
  • Use as instead:

      SomeClass obj2 = t as SomeClass; 

Obviously in the second case you would also need to perform a nullity check afterwards in case t is not a SomeClass.

EDIT: The reasoning for this is given in section 6.2.7 of the C# 4 specification:

The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. The reason for this rule is to prevent confusion and make the semantics of such conversions clear. For example, consider the following declaration:

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

If the direct explicit conversion of t to int were permitted, one might easily expect that X<int>.F(7) would return 7L. However, it would not, because the standard numeric conversions are only considered when the types are known to be numeric at binding-time. In order to make the semantics clear, the above example must instead be written:

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

This code will now compile but executing X<int>.F(7) would then throw an exception at run-time, since a boxed int cannot be converted directly to a long.

like image 131
Jon Skeet Avatar answered Sep 22 '22 05:09

Jon Skeet


In the inheritance principle of C#, interfaces could be inherited multiple times, but a class just once. As the inheritance from interfaces has complex hierarchy, the .net framework does not need to ensure the generic type T a specific interface at the compile time.(EDIT) On the contrary, a class could be ensured a specific class with declaring a type constraint at the compile as the following code.

class MyClass<T> where T : SomeClass {    void SomeMethod(T t)    {       ISomeInterface obj1 = (ISomeInterface)t;       SomeClass      obj2 = (SomeClass)t;         } } 
like image 34
Jin-Wook Chung Avatar answered Sep 19 '22 05:09

Jin-Wook Chung