2013-01-08 19 views
5

Używam C#/.NET 4.0 i biblioteki buforów protokołu (protobuf-net), która zapewnia następujące funkcje.Korzystanie z System.Type w celu wywołania ogólnej metody

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

muszę owinąć dwa z tych połączeń z odpowiednikami non-generycznych. W szczególności chcę

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

że po prostu zadzwonić do odpowiednich członków generyczne Serializer przy starcie. Dostałem metodę DeserializeReflection do pracy z następującego kodu:

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

SerializeReflection metody jest to, co jest przyczyną mnie kłopoty. I początkowo próbowali następujący kod:

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

Problem polega na tym, że część między typeof(Serializer) i .Invoke(...) nie działa. Połączenie z numerem GetMethod("Serialize") powoduje, że otrzymuję numer AmbiguousMatchException, ponieważ istnieją cztery metody o nazwie "Serialize."

Następnie próbowałem przy użyciu przeciążenie GetMethod że pobiera tablicę System.Type rozwiązać wiązanie:

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

Ale to właśnie sprawiło, że wynik GetMethodnull.

Jak mogę użyć refleksji uzyskać MethodInfo dla void Serializer.Serialize<T>(Stream, T), gdzie T jest instance.GetType()?

+1

Rozważmy ten wątek http://stackoverflow.com/questions/4035719/getmethod-for-generic-method –

+0

możliwe du plicate z [Select Right Generic Method with Reflection] (http://stackoverflow.com/questions/3631547/select-right-generic-method-with-reflection) – nawfal

+0

możliwy duplikat [Jak korzystać z odbicia, aby wywołać ogólną metodę?] (http://stackoverflow.com/questions/232535/how-to-use-reflection-to-call-generic-method) – usr

Odpowiedz

4

Spróbuj użyć następnego fragmentu kodu, aby sprawdzić, czy odpowiada on Twoim potrzebom. Tworzy zamkniętą instancję typu metody public static void Serialize<T>(Stream destination, T instance). W tym przypadku wybrać pierwszą metodę z Stream jako parametr, ale można zmienić ten predykat method.GetParameters().Any(par => par.ParameterType == typeof(Stream)) aby cokolwiek chcesz

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

Podczas gdy działa to dla tego konkretnego typu, gdzie jest dokładnie jedno przeciążenie 'Serialize', które ma' Strumień', należy pamiętać, że należy bardziej rygorystyczną kontrolę parametrów, jeśli zamierzasz uogólnić tę metodę lokalizacji odpowiednich ogólnych definicji metod na typie. – mlorbetske

+0

@llorbetske tak, na pewno. Dlatego napisałem, żeby określić predykat, który spełnia jego cretirię. W każdym razie - dziękuję za komentarz. Twoja odpowiedź jest również całkiem dobra. –

2

Dla tego typu rzeczy często metody pomocnicze użytkownik lubi to

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

aby go użyć, by zrobić

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

To też działa na to, co robię, ale odpowiedź pana Ivanova była bardzo bezpośrednia. –

Powiązane problemy