繁体   English   中英

类型转换的通用方法

[英]Generic method to type casting

我正在尝试编写通用方法来转换类型。 我想写类似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 型的 object 转换为 B 型”。 所以 CLR 知道变量o的真实类型,为什么它不能执行强制转换?

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

关于运营商解决方案的所有内容都是正确的......但这是我对您的主要问题的回答:

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

对我来说,这看起来更清晰。

如果您可以使用 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!");
            }
        }

    }
}

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

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关键字。

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

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

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

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

例如,当我编写一个标记器时,我使用了它的一个变体,其中输入是一个字符串,但是标记可以被解释为字符串、整数和双精度数。

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

如果您需要一种更通用的转换方式,我不得不怀疑这是否不是代码中的设计问题。 我认为为这些事情扩大 scope 的一个问题是,您尝试覆盖的区域越多,外人就越难知道该方法可以做什么。

我认为最重要的是,当有人专门为这项工作编写了一种方法以避免像Add(x, y)这样的情况时,只有xy的某些值才能进行强制转换。

我认为如果您自己尝试铸造,预期会有所不同,如T1 x = (T1) T2 y 然后我认为更明显的是你真的靠自己,因为你只是编造了一些演员而不是所谓的“覆盖所有演员方法”。

在这种情况下,很明显它专门处理实现IConvertible的对象,开发人员可以假设它可以很好地处理这些对象中的任何一个。

也许一个 object 面向哲学的沉重答案甚至不是每个人都会同意,但我认为这类“概念性问题”通常最终会出现在编程哲学中。

实例a是一个object到转换为B的时刻。 不是A类型,而是object 因此,不可能将objectB ,因为 CLR 无法知道o包含显式运算符。
编辑:
是的:这是解决方案:

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

现在 CLR 确切地知道, o是类型A的实例并且可以调用显式运算符。

如果没有“类型转换器”(一个手动映射所有已知类型的属性的过程,这根本不会发生),你永远不会让它工作。 您根本不能只将一个不相关的混凝土 class 浇铸到另一个混凝土上。 它会破坏单个 inheritance model (这是现代 OOP 的定义原则之一 - 阅读“钻石问题”)

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

也许不是你要做什么,但这会起作用:

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

但是好吧,这样的方法对我来说似乎没用......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM