在运行时在未知派生类型之间转换

时间:2016-06-17 05:24:40

标签: c#

假设我有一个用于多态的抽象基类,我喜欢编写一个方法将一个派生类型转换为另一个派生类型,但我不知道编译时的任何派生类型。我相信反思是解决这个问题的正确方法,但我不确定实现它的正确方法是什么。这就是我目前所处的位置。

Public Static BaseClass ConvertTo(BaseClass bc, Type type) {
    //bc is currently DerivedClass1 which implements IConvertable
    //type is currently DerivedClass2
    //Trying to convert DerivedClass1 to DerivedClass2
    return (BaseClass)ChangeType(bc, type);
}

此方法将DerivedClass1转换为DerivedClass2,但我必须为DerivedClass1实现IConvertable接口才能工作,我不满意因为它带有15个不必要的方法我必须实现。

是否有一种更优雅的方法来解决这个问题,这是一种更接近编译时间的东西?取决于DerivedClass1是否具有DerivedClass2 Cast操作符,该方法将成功或抛出运行时异常的方法。更像是:

Public Static BaseClass ConvertTo(BaseClass bc, Type type) {
     //First down cast it to bc, then sideways cast it to type.
     return (type)((bc.GetType())bc)
}

1 个答案:

答案 0 :(得分:3)

听起来您要求对用户定义的转换运算符进行反思。您可以通过询问公共静态方法并过滤到名为op_explicitop_implicit的方法,使用正确的参数和返回类型,并包括MethodAttributes.SpecialName来获得具有反思的人。然后正常调用该方法。

这里有一些示例代码我已经快速启动了 - 您可能希望添加更多的健壮性检查,包括对派生类型或基类型的转换等等......但它'开始:

using System;
using System.Linq;
using System.Reflection;

class Foo
{
    public int Value { get; set; }        
    public static explicit operator Bar(Foo x) => new Bar { Value = x.Value };
    public static explicit operator Foo(Bar x) => new Foo { Value = x.Value };
}

class Bar
{
    public int Value { get; set; }
}

class Test
{
    static void Main()
    {
        var foo = new Foo { Value = 10 };
        var bar = Convert<Bar>(foo);
        Console.WriteLine(bar.Value);
        foo = Convert<Foo>(bar);
        Console.WriteLine(foo.Value);        
    }

    public static T Convert<T>(object source)
    {
        var conversion = FindConversion(source.GetType(), typeof(T));
        if (conversion == null)
        {
            throw new InvalidOperationException("No conversion found");
        }
        return (T) conversion.Invoke(null, new[] { source });
    }

    private static MethodInfo FindConversion(Type fromType, Type toType)
    {
        var expectedParameterTypes = new[] { fromType };
        var methods = from type in new[] { fromType, toType }
                      from method in type.GetMethods(BindingFlags.Public | BindingFlags.Static)
                      where method.Name == "op_Explicit" || method.Name == "op_Implicit"
                      where (method.Attributes & MethodAttributes.SpecialName) != 0
                      where method.ReturnType == toType
                      where method.GetParameters()
                                  .Select(p => p.ParameterType)
                                  .SequenceEqual(expectedParameterTypes)
                      select method;
        return methods.FirstOrDefault();
    }
}