2013-01-08 13 views
5

Sto usando C# /. NET 4.0 e una libreria di buffer di protocollo (protobuf-net) che fornisce la seguente funzionalità.Utilizzo di System.Type per chiamare un metodo generico

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

Ho bisogno di avvolgere due di queste chiamate con gli equivalenti non generici. In particolare, voglio

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

che semplicemente chiamare i rispettivi membri generiche di Serializer in fase di esecuzione. Ho acquistato il metodo DeserializeReflection di lavorare con il seguente codice:

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

Il SerializeReflection metodo è quello che mi sta causando problemi. Ho inizialmente provato il codice seguente:

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

Il problema è che la parte tra typeof(Serializer) e .Invoke(...) non funziona. La chiamata a GetMethod("Serialize") mi ottiene un AmbiguousMatchException, perché ci sono quattro metodi denominati "Serialize".

Ho poi provato utilizzando il sovraccarico di GetMethod che accetta un array di System.Type per risolvere il binding:

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

Ma appena fatto il risultato di GetMethodnull.

Come posso utilizzare la reflection per ottenere il MethodInfo per void Serializer.Serialize<T>(Stream, T), dove T è instance.GetType()?

+1

Considerate questa discussione http://stackoverflow.com/questions/4035719/getmethod-for-generic-method –

+0

possibile du plicate di [Seleziona il metodo generico destro con Reflection] (http://stackoverflow.com/questions/3631547/select-right-generic-method-with-reflection) – nawfal

+0

possibile duplicato di [Come utilizzare il reflection per chiamare il metodo generico?] (http://stackoverflow.com/questions/232535/how-to-use-reflection-to-call-generic-method) – usr

risposta

4

Prova a utilizzare il prossimo snippet di codice per vedere se soddisfa le tue necessità. Crea un'istanza di tipo stretto con metodo public static void Serialize<T>(Stream destination, T instance). In questo caso selezionare il primo metodo con Stream come parametro, ma è possibile modificare questo predicato method.GetParameters().Any(par => par.ParameterType == typeof(Stream)) a quello che vuoi

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

Mentre questo funziona per questo tipo specifico dove c'è esattamente un sovraccarico di 'Serialize' che prende un' Stream', tenere presente che un controllo più rigoroso dei parametri deve essere eseguito se si intende generalizzare questo metodo di individuazione delle definizioni di metodo generico appropriate su un tipo. – mlorbetske

+0

@mlorbetske sì, certo. Ecco perché ho scritto per specificare il predicato, che soddisfa la sua cretiria. Comunque, grazie per il commento. Anche la tua risposta è abbastanza buona. –

2

Per questo genere di cose che spesso i metodi utente di supporto come questo

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

per usarlo, faresti

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

Ciò funzionerebbe anche per quello che sto facendo, ma la risposta di Ivanov è stata molto diretta. –

Problemi correlati