简体   繁体   中英

Converting between unknown derived types at runtime

Lets say I have an abstract base class used for polymorphism and I like to write a method to convert one derived type to another, but I don't know either of the derived types at compile time. I believe reflection would be the right way to solve this, but I'm not sure what the correct way to implement it would be. Here's where I'm currently stuck.

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

This method will convert DerivedClass1 to DerivedClass2 but I had to Implement the IConvertable Interface for DerivedClass1 for it to work, which I'm unhappy with because it comes with 15 unnecessary methods I have to implement.

Is there a more elegant way to solve this, something that's closer to compile time casting? One where the method would either succeed or throw a run time exception depending on if DerivedClass1 had a DerivedClass2 Cast Operator. Something more like:

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

It sounds like you're asking for reflection over user-defined conversion operators. You can get at those with reflection by asking for public static methods and filtering to ones called op_explicit or op_implicit , with the right parameter and return types, and including MethodAttributes.SpecialName . Then just invoke the method as normal.

Here's some sample code I've whipped up quickly - you may well want to add more checks for robustness, include conversions to derived types or from base types, etc... but it's a start:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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