Question

I've some issues with the following piece of code. I would like to explicit a string to an object, this is working perfectly fine, however, if this object is part of a generic class, this is failing with the following error exception: "Unable to cast object of type 'System.String' to type 'test.B'". Even though I've overloaded the the method.

using System;
using System.Collections.Generic;

namespace test {
    class Program {
        static void Main(string [] args) {
            // These two cast perfectly fine.
            B x = (B) "abc";
            C y = (C) "def";

            A <B> a = new A<B>();
            a.b();
            A <C> b = new A<C>();
            b.b();
        }
    }

    class A<T> {
        public List <T> a = new List<T>();

        public void b() {
            // Unable to cast object of type 'System.String' to type 'test.B'
            this.a.Add ((T) (object) "abc"); 
            this.a.Add ((T) (object) "def");
            this.a.Add ((T) (object) "ghi");
        }
    }

    class B {
        public string b;

        public static explicit operator B(string a) {
            B x = new B();
            x.b = a;
            return x;
        }
    }

    class C {
        public string c;

        public static explicit operator C(string a) {
            C x = new C();
            x.c = a;
            return x;
        }
    }
}

It would be awesome if somone could explain to me why this isn't casting properly.

Thanks

Was it helpful?

Solution

Conversion operators only apply when the type is known statically; after all, generic methods need to use the exact same IL for every T - so it can't call your operator in some cases, and a type-check in others.

Additionally, because you've explicitly cast to object, it will never be used; a cast from object is always a simple unbox or type check.

An evil fix would be (and I don't like this):

        this.a.Add((T)(dynamic)"abc");
        this.a.Add((T)(dynamic)"def");
        this.a.Add((T)(dynamic)"ghi");

which defers the resolution to runtime. It works, but I would need to wash my eyes after that. More generally, though: operators and generics do not play nicely - so: try not to use that combination in your API. I really wouldn't use the above, personally!

OTHER TIPS

Compiler has no knowledge at compile time if generic parameter has explicit cast you are using.

You can introduce interface though, and put constrain on generic parameter

You inserted the (object) cast to get the compiler to stop telling you that you were doing it wrong. That worked, the compiler can no longer complain because that stopped it from being able to check types. And a cast from object to T might actually work, as small as the odds might be.

What you however didn't count on is that explicit conversion operators are a C# language feature. Only the compiler knows what method to execute when such a cast is applied. It is not a CLR feature, the runtime does not go hunting and gathering to try to find an applicable method to make the conversion. The C# compiler is otherwise powerless to use any of the operators you provided at compile time, it doesn't know the type of T.

Trouble is, the compiler is no longer around to help when the cast executes. Kaboom.

Having generic types applied at runtime instead of compile time is a .NET feature, the generic term is "reified generics". As opposed to "type erasure", the way generics are implemented in Java and C++. The trouble with type erasure is that all info about the generic type arguments are lost after code is compiled, a generic type can't be used by another language and Reflection doesn't work. The trouble with reified generics is that operations that cannot be universally applied to any type cannot be used. Like operator+(). And like this cast.

Its because T for A<T> does not know about the explicit cast operators for B or C.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top