2013-01-08 20 views
5

Ich verwende C# /. NET 4.0 und eine Protocol Buffers-Bibliothek (Protobuf-Net), die die folgende Funktionalität bietet.Verwenden von System.Type zum Aufrufen einer generischen Methode

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

Ich muss mit Nicht-Generika zwei dieser Anrufe wickeln. Insbesondere möchte ich

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

, die einfach die jeweiligen Gattungs Mitglieder Serializer zur Laufzeit aufrufen. Ich habe die DeserializeReflection Methode bekommen mit dem folgenden Code zu arbeiten:

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

Die SerializeReflection Methode ist es, was mir Probleme verursacht. Ich versuchte zunächst, den folgenden Code:

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

Das Problem ist, dass der Teil zwischen typeof(Serializer) und .Invoke(...) funktioniert nicht. Der Aufruf an GetMethod("Serialize") ruft mir eine AmbiguousMatchException, weil es vier Methoden namens "Serialize" gibt.

Ich habe dann versucht, die Überlastung von GetMethod verwenden, das eine Reihe von System.Type nimmt die Bindung zu lösen:

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

Aber dies machte nur das Ergebnis von GetMethodnull.

Wie kann ich Reflektion verwenden die MethodInfo für void Serializer.Serialize<T>(Stream, T) zu bekommen, wo Tinstance.GetType() ist?

+1

Betrachten dieses Themas http://stackoverflow.com/questions/4035719/getmethod-for-generic-method –

+0

möglich du plicaate von [Wählen Sie rechts generische Methode mit Reflexion] (http://stackoverflow.com/questions/3631547/select-right-generic-method-with-reflection) – nawfal

+0

mögliche Duplikate von [Wie Reflection verwenden, um generische Methode zu nennen?] (http://stackoverflow.com/questions/232535/how-to-use-reflection-to-call-generic-method) – usr

Antwort

4

Versuchen Sie, das nächste Code-Snippet zu verwenden, um zu sehen, ob es Ihren Anforderungen entspricht. Es erstellt eine eng typisierte Instanz der Methode public static void Serialize<T>(Stream destination, T instance). In diesem Fall wählen Sie die erste Methode mit Stream als Parameter, aber Sie können dieses Prädikat method.GetParameters().Any(par => par.ParameterType == typeof(Stream)) ändern, was Sie wollen

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

Während dies für diesen spezifischen Typ funktioniert, wo es genau eine Überladung von 'Serialize' gibt, die ein' Beachten Sie, dass eine strengere Überprüfung der Parameter vorgenommen werden sollte, wenn Sie diese Methode zum Suchen der geeigneten generischen Methodendefinitionen für einen Typ verallgemeinern möchten. – mlorbetske

+0

@mlorbetske ja, sicher. Deshalb habe ich geschrieben, um Prädikat zu spezifizieren, das seine Kretirias erfüllt. Wie auch immer - danke für den Kommentar. Deine Antwort ist auch ziemlich gut. –

2

für diese Art der Sache, die ich oft Methoden Benutzer Helfer wie diese

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

Um es zu nutzen, würden Sie tun

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

Das würde auch für das funktionieren, was ich mache, aber die Antwort von Herrn Ivanov war sehr direkt. –

Verwandte Themen