Generic method to type casting

11,797

Solution 1

If you can use c# 4.0 this works:

namespace CastTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {

            A a = new A();
            B b = Cast.To<B>(a);
            b.Test();

            Console.Write("Done.");
            Console.ReadKey();
        }

        public class Cast
        {
            public static T To<T>(dynamic o)
            {
                return (T)o;
            }
        }

        public class A
        {
            public static explicit operator B(A a)
            {
                return new B();
            }
        }

        public class B
        {
            public void Test()
            {
                Console.WriteLine("It worked!");
            }
        }

    }
}

Solution 2

All of what's been said about the operator resolution is correct...but this is my answer to your main question:

    public static T To<T>(this object o)
    {
        return (T)(dynamic)o;
    }

The key here is that casting o to dynamic will force the .NET to search for the explicit operator at runtime.

Plus, why not make it an extension method?

Instead of

        A a = new A();
        B b = Cast.To<B>(a);

you can do

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

An added benefit of exposing it as an extension method is that you gain a fluent interface for explicit casting (if you like that sort of thing). I've always hated the amount of nested parenthesis balancing required for explicit casting in .NET.

So you can do:

a.To<B>().DoSomething().To<C>().DoSomethingElse() 

instead of

((C)((B)a).DoSomething())).DoSomethingElse()

which, to me, looks clearer.

Solution 3

You can do this trick by finding the right methods through Reflection:

public static T To<T> (object obj)
{
    Type sourceType = obj.GetType ();
    MethodInfo op = sourceType.GetMethods ()
                    .Where (m => m.ReturnType == typeof (T))
                    .Where (m => m.Name == "op_Implicit" || m.Name == "op_Explicit")
                    .FirstOrDefault();

    return (op != null)
        ? (T) op.Invoke (null, new [] { obj })
        : (T) Convert.ChangeType (obj, typeof (T));
}

In .NET 4.0, you can use dynamic keyword as suggested in other answers.

Solution 4

Your Cast.To<T>() is just trying to interpret reference to given object as reference to T. Which fails of course.

And if compiler encounters (B) a and knows that a is of type A and type A has compile-time cast operator to type B - it emits this cast. It is not your case.

Solution 5

You will never get this to work without a 'type converter'(a manual process of mapping across attributes for all known types which simply will not happen). You simply cannot just cast one non-related concrete class to another. It would break the single inheritance model (which is one of the defining principles of modern OOP - read up on 'the Diamond Problem')

It was also noted about interfaces (polymorphism) - both classes would have to derive from the same interface also (which is along the same lines)

Share:
11,797
xzt
Author by

xzt

Updated on June 23, 2022

Comments

  • xzt
    xzt almost 2 years

    I'm trying to write generic method to cast types. I want write something like Cast.To<Type>(variable) instead of (Type) variable. My wrong version of this method:

    public class Cast
    {
        public static T To<T>(object o)
        {
            return (T) o;
        }
    }
    

    And this is simple test:

    public class A
    {
        public static explicit operator B(A a)
        {
            return new B();
        }
    }
    
    public class B
    {
    }
    
    A a = new A();
    B b = Cast.To<B>(a);
    

    As you guessed, this code will fail with InvalidCastException.

    Is this code fail because virtual machine doesn't know how to cast variable of type object to type B at run-time? But exception message says: "unable to cast object of type A to type B". So CLR knows about real type of variable o, why it cannot perform casting?

    And here is main question: how should I rewrite method T To<T>(object o) to fix this problem?

  • Ivan Danilov
    Ivan Danilov almost 13 years
    Not correct. It is possible to cast object to B. If object variable contains instance of B. It is impossible to determine that it is required to call explicit conversion method to cast given object to B - that's right.
  • Kirk Woll
    Kirk Woll almost 13 years
    @Ivan, you misunderstand @DotNET's point: it is that the explicit operator is registered against A and not object.
  • Kirk Woll
    Kirk Woll almost 13 years
    However, this still won't work. It will result in the compiler error, Cannot convert type 'T2' to 'T1'. This is because operator overload resolution happens at compile time and when the Cast class is compiled, T1 is only object and so still fails to pick up the converter.
  • CodingGorilla
    CodingGorilla almost 13 years
    @Ivan is correct, DotNET is correct, he just slightly misstated it.
  • Ivan Danilov
    Ivan Danilov almost 13 years
    @Kirk Woll: It seems it is a game of words. By "cast" I mean getting value of type B from value of another type. And not calling cast operator.
  • CodingGorilla
    CodingGorilla almost 13 years
    @Kirk is correct that it still wont work, because at compile time the compiler cannot guarantee that T2 has the proper operator necessary to do the conversion.
  • Kirk Woll
    Kirk Woll almost 13 years
    Interestingly, even if you add the appropriate where clauses (where T2 : A where T1 : B) it still doesn't allow the cast. There must be some subtle rules about this sort of thing in the C# language spec. Eric or Jon, where are you? ;)
  • Ivan Danilov
    Ivan Danilov almost 13 years
    The diamond problem is only slightly touches these things. Here it is all about type system and type safety. Any of these types could be interfaces, btw. So what's then with single inheritance? :)
  • Kirk Woll
    Kirk Woll almost 13 years
    What do you mean by TypeConverter? The OP does have a user type conversion operator registered. If you actually mean System.ComponentModel.TypeConverter that seems beside the point as this is language question, not an API question.
  • CodingGorilla
    CodingGorilla almost 13 years
    @Kirk I don't think the compiler does that much work to search the classes to see if they have compatible operators, seems like that would be a lot of work on the compiler's side.
  • Ivan Danilov
    Ivan Danilov almost 13 years
    I think that TypeConverter was mentioned here because of principle rather than because of concrete implementation. You really should have such class if you need to convert classes from different parts of class hierarchy. So TypeConverter point seems correct to me.
  • Ivan Danilov
    Ivan Danilov almost 13 years
    Wow. +1 just for working example. It seems it is a problem in this topic :) But you're right. It is slow and useless.
  • Ivan Danilov
    Ivan Danilov almost 13 years
    Nooo! Please do not introduce dynamic here! It is not overkill, it is OVERKILL :))
  • CodingGorilla
    CodingGorilla almost 13 years
    Extension method is a good idea if you're only going to do this with a handful of classes, but anymore than a few would make an awful lot of work.
  • Ivan Danilov
    Ivan Danilov almost 13 years
    @Kirk Woll: what if you have derivative of A that has different casting operators? Generic restrictions are just don't guaranteeing correct things.
  • Jeff
    Jeff almost 13 years
    I like the fact that having an extension method because it means you expose a fluent interface for explicit casting (I've always hated the amount of nested parenthesis balancing required for explicit casting in .NET. So you can do: a.To<B>().DoSomething().To<C>().DoSomethingElse() instead of ((C)((B)a).DoSomething())).DoSomethingElse
  • Ivan Danilov
    Ivan Danilov almost 13 years
    Wow, stop. I missed the problem here :) It is the same as with first DotNET's attempt. Compiler doesn't know a way to cast sourceType to targetType. Aaaargh! Your +1 is locked now, I can't take it back. Lucky you, inattentive me))
  • Ivan Danilov
    Ivan Danilov almost 13 years
    Yes, but you're explicitly saying to compiler that it should perform checks in runtime. And thus it is compiling. Without dynamic if it compiles - it just can't throw things. Except those your cast operator throws of course.
  • Johnny5
    Johnny5 almost 13 years
    It's not really the same thing, you have to provide the real type of the object (sourceType) and the type to cast to. That implies sourceType can be explicitely cast to target type. It is the same as public static T1 To<T1, T2> (T2 o) { return (T1) o; }
  • Johnny5
    Johnny5 almost 13 years
    With my first exemple, you would do A a = new A (); B b = Cast.To<A, B> (a);. It works, but it is useless.
  • Ivan Danilov
    Ivan Danilov almost 13 years
    No, it can't. In compile-time for compiler these are just two generic types. Imagine A and B are placed in different assembly. And compiler don't know if conversion from sourceType to targetType exists because usages could be not written yet. And it tells (copy&paste from real compiler): "Cannot convert type 'sourceType' to 'targetType'". Period.
  • Paul Sullivan
    Paul Sullivan almost 13 years
    I have edited to include the phrase 'concrete class' Ivan and yes type converter is meant in the generic term of mapping attributes across (which would mean we would have to do so for all known and unknown types which is of course impossible).
  • xzt
    xzt almost 13 years
    Thanks for Reflection solution. But it's not truly complete. I have one more test for your: double d = 1.5; int i = Cast.To<int>(d);. This will fail with InvalidCastException.
  • xzt
    xzt almost 13 years
    Thanks for explanation about operator resolution at compile-time. You've answered my first question.
  • xzt
    xzt almost 13 years
    I'm afraid I don't understand how described problem and single inheritance model intersect here. But I think I've understood your thoughts about "type converter". I agree with your in this case, using IConvertible or TypeConverter or custom "type converter" won't be a bad idea.
  • xzt
    xzt almost 13 years
    Yep, I know about method extensions. To be honest, this problem with generic type casting was born exactly when I want more cleaner syntax to casts as you said above. I agree with you here and I also don't like tons of parenthesis and want to write something like a.To<B>(). But method extensions are moot point and applying ones to object type isn't a good idea.
  • xzt
    xzt almost 13 years
    Thanks for solving main problem!
  • Ivan Danilov
    Ivan Danilov almost 13 years
    @shidzo: Here is the reference to C# spec (somewhat obsolete, but no significant changes there related to our discussion here were made since then): msdn.microsoft.com/en-us/library/aa691370%28v=vs.71%29.aspx
  • Ivan Danilov
    Ivan Danilov almost 13 years
    @Paul: Weeell, I still think that Diamond problem has nothing to do here, but with your last edits I've taken my -1 away.
  • Jeff
    Jeff almost 13 years
    Sure, overusing extension methods applied to the object type isn't a good idea...but I'd say this one for casting is more than ok.
  • Aaron Anodide
    Aaron Anodide about 12 years
    this is interesting - what's dynamic doing here?
  • Niklas
    Niklas about 7 years
    Could you explain more about the use of the dynamic keyword here ? Ive looked around and just managed to find out that explicit casting requires an operator that does that...