4

In C#, the ability to cast between signed and unsigned integer types appears to be influenced by:

  1. Whether a scalar or an array type is being converted.
  2. Whether the variable is declared as an object.

Consider the following code example:

// If the variable is declared as a byte array then type casting to sbyte[] results in a 
// compile-time error.
byte[] byteArray = new byte[2];
var c = (sbyte[])byteArray; // Compilation eror

// But if the variable is declared as an object then we neither get a compile-time nor a 
// run-time error
object byteArrayObject = new byte[2];
var a = (sbyte[])byteArrayObject;

// With an explicitly typed scalar, the byte -> sbyte type conversion succeeds with no errors
byte scalarByte = 255;
var b = (sbyte)scalarByte;

// But if the scalar is declared as an object, an InvalidCastException is thrown at run-time
object byteObject = (byte)4;
var e = (sbyte)byteObject; // InvalidCastException

To summarize:

  • Array declared as byte[]: failure
  • Array declared as object: success
  • Scalar declared as byte: success
  • Scalar declared as object: failure

While this example only considers bytes, the same pattern appears to hold true for other integer types. Can anyone explain why these results are so inconsistent?

  • This link provides answer to first part (about arrays): http://stackoverflow.com/a/33896378/5311735. This to the second: https://blogs.msdn.microsoft.com/ericlippert/2009/03/19/representation-and-identity/ – Evk Dec 05 '16 at 12:41
  • Only the 2nd example is messed up, imo. Arrays should not be covariant, but they are: http://stackoverflow.com/questions/4317459 – Dennis_E Dec 05 '16 at 12:42
  • A better title for your question is "how does explicit conversion work in C# when arrays are involved, and how does it work when boxing is involved"? These are two questions, and signed vs. unsigned integer is entirely irrelevant. – Jeroen Mostert Dec 05 '16 at 13:57

1 Answers1

2

The second case has nothing to do with signed or unsigned types. You simply can not unbox a value type to something that is not its exact type. This will also fail:

object i = 1;
var l = (long)i; //Runtime expection: unboxing an int to a long

The first case is an unfortunate mismatch between allowed conversiones in C# and the CLR; C# disallows value type array variance (again, it's more general than signed and unsigned types). When casting from object the compiler can't disallow it because it simply doesn't have enough information to do so, and the CLR then succeeds.

Do note though, that this only happens with value type arrays. Reference type arrays are variant in C# (unfortunately):

var strs = new string[];
var objs = (object[])strs; //Compiles just fine.

This is unfortunate because it's a broken variance; nobody stops you from doing this:

objs[0] = new object(); //Runtime exception, an object is not a string. Ouch!

Also of interest is that c# generic type variance in interfaces and delegates doesn't work with value types either. A good explanation of why this is so can be found here: here

Community
  • 1
  • 1
InBetween
  • 32,319
  • 3
  • 50
  • 90
  • "You simply can not unbox a value type to something that is not its exact type." - minor caveat: enum vs integer unboxing works between types, as long as the *underlying* type matches – Marc Gravell Dec 05 '16 at 14:01
  • Enums are the odd ducks out here -- even unboxing between different enum types works, as long as they have the same underlying type. The CLR's belief in enums as distinct types is a bit shaky, really. – Jeroen Mostert Dec 05 '16 at 14:14