mercredi 3 juin 2020

Type.GetMethod() for polymorphic method (both generic and non-generic)

I am currently creating a custom way of deep-copying my objects. I use a static class for this functionality.

public static class CopyServer
{

    public static int CopyDeep(int original)
    {
        return original;
    }
    //not shown: same for all other value types I use (long, float,...)


    public static T CopyDeep<T>(T original) where T: ICopyAble
    {
        if (original == null)
            return default;
        if (original is ICopyAutofields)
            return CopyAutofields(original);
        return (T)original.CopyDeep();
    }

    private static T CopyAutofields<T>(T original)
    {
        Delegate del;
        if (!_copyFunctions.TryGetValue(typeof(T), out del))
        {
            //not shown: Building expression for parameter etc.
            foreach (var fieldInfo in typeof(T).GetFields())
            {
                //not shown: checking options set by custom attributes

                MethodInfo methodInfo = typeof(CopyServer).GetMethod("CopyDeep", new[] { fieldInfo.FieldType });
                //I can't remove the second param without getting an AmbiguousMatchException

                if (methodInfo == null)
                {
                    throw new Exception($"CopyDeep not defined for type {fieldInfo.FieldType}");
                }

                if (methodInfo.IsGenericMethod)
                    methodInfo = methodInfo.MakeGenericMethod(fieldInfo.FieldType);

                Expression call = Expression.Call(methodInfo, readValue);

                //not shown: Assign Expression

            }

            //not shown: return Expression and compiling
        }
        return ((Func<T, T>)del)(original);
    }
}

I use T CopyAutofields<T> to build functions (by building and compiling Expression Trees) so I don't have to create copy-functions for each and every class I want to copy by hand. I control the copy-behaviour with Custom Attributes (I left this part in the code above since it is not relevant for my problem).

The code works fine as long as long as only fields with types for which a non-generic function exists are used. But it is not able to retrieve my generic function T CopyDeep<T>.

Example:

//This works:
public class Manager : ICopyAble,ICopyAutofields
{
    public string FirstName;
    public string LastName;
}

//This doesn't
//Meaning: typeof(CopyServer).GetMethod("copyDeep", new[] { fieldInfo.FieldType }); 
//in T copyAutofields<T> returns null for the Manager-field and my exception gets thrown
public class Employee : ICopyAble,ICopyAutofields
{
    public string FirstName;
    public string LastName;
    public Manager Manager;
}

//This is what I was using before I started using the ICopyAutofields. 
//This approach works, but its' too much too write since my classes usually 
//have way more than three fields and I occasionally forget to update 
//copyDeep()-function if I add new ones.
public class Employee : ICopyAble,ICopyAutofields
{
    public string FirstName;
    public string LastName;
    public Manager Manager;

    public IModable CopyDeep()
    {
        var result = new Employee();
        result.FirstName = CopyServer.copyDeep(FirstName);
        result.LastName= CopyServer.copyDeep(LastName);
        result.Manager= CopyServer.copyDeep(Manager);
        return result;
    }
}

Long story short: I need a way of getting a matching function for a type T if both generic and non-generic functions with the right name exist.





Aucun commentaire:

Enregistrer un commentaire