Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why there is a restriction for explicit casting a generic to a class type but there is no restriction for casting a generic to an interface type?

Tags:

c#

generics

While reading Microsoft documentation, I stumbled on such an interesting code sample:

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

It means you can cast your generic to the interface explicitly but not to the class unless you have a constraint. Well, I still cannot understand the logic behind the decision as both interface and class type castings are throwing exceptions, so why would one protect against only one of these exceptions?

BTW- there is a way around the compile error but this does not remove the logic mess in my head:

class MyOtherClass
{...}

class MyClass<T> 
{

   void SomeMethod(T t)

   {
      object temp = t;
      MyOtherClass obj = (MyOtherClass)temp;

   }
}
like image 347
Yurii Hohan Avatar asked Nov 01 '11 08:11

Yurii Hohan


People also ask

What is the purpose of the class constraints on a type Parameter?

Object, you'll apply constraints to the type parameter. For example, the base class constraint tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called in the generic class.

What are generic constraints in c#?

The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.


2 Answers

That's exactly what you get in normal circumstances - without generics - when you try to cast between classes with no inheritance relationship:

 public interface IA
 {
 }

 public class B
 {
 }

 public class C
 {
 }

 public void SomeMethod( B b )
 {
     IA o1 = (IA) b;   <-- will compile
     C o2 = (C)b;  <-- won't compile
 }

So without a constraint, the generic class will behave as if there is no relationship between the classes.

Continued...

Well, let's say someone does this:

 public class D : B, IA
 {
 }

And then calls:

SomeMethod( new D() );

Now you'll see why the compiler lets the interface cast pass. It really can't know at compile time if an interface is implemented or not.

Remember that the D class may very well be written by someone who is using your assembly - years after you compiled it. So there is no chance that the compiler can refuse to compile it. It must be checked at run time.

like image 122
Dan Byström Avatar answered Oct 20 '22 22:10

Dan Byström


The big difference is that an interface is guaranteed to be a reference type. Value types are the trouble makers. It is explicitly mentioned in the C# Language Specification, chapter 6.2.6, with an excellent example that demonstrates the problem:


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.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 compile 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.F(7) would then throw an exception at runtime, since a boxed int cannot be converted directly to a long.

like image 27
Hans Passant Avatar answered Oct 20 '22 22:10

Hans Passant