5

I am using C#/.NET 4.0 and a Protocol Buffers library (protobuf-net) which provides the following functionality.

public static class Serializer {
    public static void Serialize<T>(Stream destination, T instance);
    public static void Serialize<T>(SerializationInfo info, T instance);
    public static void Serialize<T>(XmlWriter writer, T instance);
    public static void Serialize<T>(SerializationInfo info, StreamingContext context, T instance);
    public static T Deserialize<T>(Stream source);
}

I need to wrap two of these calls with non-generic equivalents. Specifically, I want

void SerializeReflection(Stream destination, object instance);
object DeserializeReflection(Stream source, Type type);

that simply call the respective generic members of Serializer at runtime. I have gotten the DeserializeReflection method to work with the following code:

public static object DeserializeReflection(Stream stream, Type type)
{
    return typeof(Serializer)
        .GetMethod("Deserialize")
        .MakeGenericMethod(type)
        .Invoke(null, new object[] { stream });
}

The SerializeReflection method is what is causing me trouble. I at first tried the following code:

public static void SerializeReflection(Stream stream, object instance)
{
    typeof(Serializer)
        .GetMethod("Serialize")
        .MakeGenericMethod(instance.GetType())
        .Invoke(null, new object[] { stream, instance });
}

The problem is that the part between typeof(Serializer) and .Invoke(...) is not working. The call to GetMethod("Serialize") gets me an AmbiguousMatchException, because there are four methods named "Serialize."

I then tried using the overload of GetMethod that takes an array of System.Type to resolve the binding:

GetMethod("Serialize", new[] { typeof(Stream), instance.GetType() })

But this just made the result of GetMethod null.

How can I use reflection to get the MethodInfo for void Serializer.Serialize<T>(Stream, T), where T is instance.GetType()?

3

2 Answers 2

4

Try to use next code snippet to see if it meets your need. It creates a close typed instance of method public static void Serialize<T>(Stream destination, T instance). In this case it select the first method with Stream as parameter, but you can change this predicate method.GetParameters().Any(par => par.ParameterType == typeof(Stream)) to whatever you want

public static object DeserializeReflection(Stream stream, object instance)
{
   return typeof(Serializer)
        .GetMethods()
        .First(method => method.Name == "Serialize" && method.GetParameters().Any(par => par.ParameterType == typeof(Stream)))
        .MakeGenericMethod(instance.GetType())
        .Invoke(null, new object[] { stream, instance });
}
Sign up to request clarification or add additional context in comments.

2 Comments

While this works for this specific type where there's exactly one overload of Serialize that takes a Stream, keep in mind that more rigorous checking of the parameters should be done if you intend to generalize this method of locating the appropriate generic method definitions on a type.
@mlorbetske yes, sure. That's why I wrote to specify predicate, that meets his cretiria. Anyway - thank for comment. Your answer is also quite good.
2

For this sort of thing I often user helper methods like this

public static MethodInfo MakeGenericMethod<TSourceType>(Type genericArgument, string methodName, Type[] parameterTypes, params int[] indexesWhereParameterIsTheGenericArgument)
{
    //Get the type of the thing we're looking for the method on
    var sourceType = typeof (TSourceType);
    //Get all the methods that match the default binding flags
    var allMethods = sourceType.GetMethods();
    //Get all the methods with the same names
    var candidates = allMethods.Where(x => x.Name == methodName);

    //Find the appropriate method from the set of candidates
    foreach (var candidate in candidates)
    {
        //Look for methods with the same number of parameters and same types 
        //   of parameters (excepting for ones that have been marked as 
        //   replaceable by the generic parameter)
        var parameters = candidate.GetParameters();
        var successfulMatch = parameters.Length == parameterTypes.Length;

        if (successfulMatch)
        {
            for (var i = 0; i < parameters.Length; ++i)
            {
                successfulMatch &= parameterTypes[i] == parameters[i].ParameterType || indexesWhereParameterIsTheGenericArgument.Contains(i);
            }
        }

        //If all the parameters were validated, make the generic method and return it
        if (successfulMatch)
        {
            return candidate.MakeGenericMethod(genericArgument);
        }
    }

    //We couldn't find a suitable candidate, return null
    return null;
}

To use it, you'd do

var serializeMethod = MakeGenericMethod<Serializer>(instance.GetType(), "Serialize", new[]{typeof(stream), typeof(object)}, 1);

1 Comment

This would also work for what I'm doing, but Mr. Ivanov's answer was very direct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.