输入类型的通用方法

时间:2011-07-22 19:57:53

标签: c# .net generics casting types

我正在尝试编写通用方法来转换类型。我想写一些像Cast.To<Type>(variable)而不是(Type) variable的东西。 我错误的这种方法版本:

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

这是一个简单的测试:

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);

正如您所猜测的,此代码将失败并显示InvalidCastException

此代码是否失败,因为虚拟机在运行时不知道如何将object类型的变量转换为B类型?但是异常消息说:“无法将类型A的对象转换为类型B”。所以CLR知道变量o的实际类型,为什么它不能执行转换?

这是一个主要问题:我应该如何重写方法T To<T>(object o)来解决这个问题?

8 个答案:

答案 0 :(得分:15)

所有关于操作员解决方案的说法都是正确的......但这是我对你主要问题的回答:

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

这里的关键是将o转换为dynamic会强制.NET在运行时搜索显式运算符。

另外,为什么不把它作为一种扩展方法?

而不是

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

你可以做到

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

将它作为扩展方法公开的另一个好处是,您可以获得用于显式转换的流畅界面(如果您喜欢这种事情)。我总是讨厌.NET中显式转换所需的嵌套括号平衡量。

所以你可以这样做:

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

而不是

((C)((B)a).DoSomething())).DoSomethingElse()
对我来说,这看起来更清楚。

答案 1 :(得分:12)

如果您可以使用c#4.0,则可以使用:

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!");
            }
        }

    }
}

答案 2 :(得分:4)

你可以通过反思找到正确的方法来做到这一点:

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));
}

在.NET 4.0中,您可以按照其他答案中的建议使用dynamic关键字。

答案 3 :(得分:2)

你的Cast.To<T>()只是试图将对给定对象的引用解释为对T.的引用。当然这是失败的。

如果编译器遇到(B) a并且知道a类型为A且类型A具有编译时强制转换运算符以键入B - 它发出这个演员。这不是你的情况。

答案 4 :(得分:1)

实例a是投射到B时的对象。不是A类型,而是object。因此,由于CLR无法知道object包含显式运算符,因此无法将B转换为o修改
是啊!这是解决方案:

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

现在CLR完全知道,oA类型的实例,可以调用显式运算符。

答案 5 :(得分:1)

如果没有“类型转换器”(对于所有已知类型的属性进行映射的手动过程,这根本不会发生),您将永远无法工作。您根本无法将一个不相关的具体类转换为另一个。它将破坏单一继承模型(这是现代OOP的定义原则之一 - 阅读'钻石问题')

还注意到接口(多态) - 两个类也必须从同一个接口派生(沿着相同的行)

答案 6 :(得分:1)

我实际上遇到过这个问题不止一次,当我可以将自己限制为实现IConvertible接口的类型时,它感觉不是OOP“脏”。然后解决方案实际上变得非常干净!

private T To<T>(object o) where T : IConvertible
{
    return (T)Convert.ChangeType(o, typeof(T));
}

当我编写一个tokenizer时,我使用了这种变体,其中输入是一个字符串,但令牌可以解释为字符串,整数和双精度。

由于它使用Convert类,编译器实际上会有信息知道该怎么做。这不仅仅是一个简单的演员。

如果你需要一种更通用的方法,我不得不想知道这不是代码中的设计问题。我认为扩大这些问题的范围的一个问题是,你试图覆盖的区域越多,外人就越难以知道该方法可以做多少。

我认为最重要的是,如果有人专门为工作编写了一种方法,以避免出现Add(x, y)这样的情况,只有x和{{1}的某些值,那么这种情况真的很有效。 }。

我认为如果你自己尝试投射,期望是不同的,如y。然后我认为你更真实地依靠自己,因为你只是编造了一些演员,而不是称为“覆盖所有演员方法”。

在这种情况下,很明显它专门处理实现T1 x = (T1) T2 y的对象,开发人员可以假设它可以很好地处理任何这些对象。

也许一个面向对象 - 哲学的重要答案,并不是每个人都会同意,但我认为这些“概念问题”往往最终出现在编程哲学中。

答案 7 :(得分:0)

也许不是你要做什么,但这样可行:

public class Cast
{
    public static targetType To<soureType, targetType>(object o)
    {
        return (targetType)((sourceType) o);
    }
}

但是,这种方法对我来说似乎毫无用处......