2011-07-10 15 views
7

Mam potrzebę wywoływania metod na typ poprzez odbicie za pomocą C#.Jak wykonać automatyczną konwersję typów parametrów podczas wywoływania metody za pomocą odbicia w języku C#?

W czasie wykonywania moje dane będą składać się ze Słownika zawierającego pary nazwa/wartość. Nazwy w Słowniku będą odpowiadać nazwom parametrów w metodzie, którą wywołam. Ponadto, w czasie wykonywania, będę mieć arbitralny kwalifikowany typ nazwy zespołu i nazwę metody. W czasie projektowania nie będę miał wiedzy o typie i metodzie innej niż metoda akceptująca zmienną liczbę parametrów typu int, string, DateTime, bool, int [], string [], DateTime [] lub bool [].

Nie mam problemu z dojściem do punktu, w którym mogę utworzyć instancję typu za pomocą refleksji i wywołać metodę. Siedzę w miejscu, gdzie mam do konwersji wartości ciągu w moim słowniku, do odpowiedniego typu potrzebnego metodą kiedy rozmowy:

someMethodInfo.Invoke(instance, new [] { ... }) 

wiem, że muszę chyba wyliczyć poprzez MethodInfo.GetParameters() i wykonać konwersję typu dla każdego parametru. Próbuję dowiedzieć się, jak to zrobić, i najlepiej, jak zrobić to skutecznie.

Moje dotychczasowe badania dotyczyły kopania kodu źródłowego MVC, podobnie jak w przypadku przekazywania wartości formularza do metody ActionMethod. Znalazłem ActionMethodDispatcher, ale używają wyrażeń LINQ, z którymi jestem nieznany.

Przyjrzałem się także podobnym pytaniom na temat SO, ale nie znalazłem niczego, co by odpowiedziało na moje pytanie.

Z zadowoleniem przyjmuję wszelkie wskazówki dotyczące rozwiązania.

+0

Może być lepsze sposoby, aby przejść, ale użyłem [System.Convert] (http://msdn.microsoft.com/en-us/library/system. convert.aspx) dla podobnych potrzeb. Radzenie sobie z typami nullabe dodatkowo zwiększa dodatkową złożoność. – Clayton

+0

Inną opcją dla bardziej arbitralnej konwersji jest [TypeConverter] (http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter (v = VS.100) .aspx), chociaż nie jestem pewnie, jak to by działało z tablicami. –

+0

@Clayton Tak, myślę, że System.Convert będzie moim rozwiązaniem, jeśli nie będzie nic bardziej eleganckiego.Wydaje się, że może istnieć bardziej efektywny sposób niż przechodzenie między poszczególnymi typami parametrów i używanie System.Convert w przełączniku (...) do wykonywania konwersji poszczególnych typów. –

Odpowiedz

3

Oto kod, który może być stosowany do przeliczenia parametrów:

public object ConvertSingleItem(string value, Type newType) 
{ 
    if (typeof(IConvertible).IsAssignableFrom(newType)) 
    { 
     return Convert.ChangeType(value, newType); 
    } 
    else 
    { 
     // TODO: Add custom conversion for non IConvertible types 
     var converter = CustomConvertersFactory.GetConverter(newType); 
     return converter.Convert(value); 
    } 
} 

public object ConvertStringToNewNonNullableType(string value, Type newType) 
{ 
    // Do conversion form string to array - not sure how array will be stored in string 
    if (newType.IsArray) 
    { 
     // For comma separated list 
     Type singleItemType = newType.GetElementType(); 

     var elements = new ArrayList(); 
     foreach (var element in value.Split(',')) 
     { 
      var convertedSingleItem = ConvertSingleItem(element, singleItemType); 
      elements.Add(convertedSingleItem); 
     } 
     return elements.ToArray(singleItemType); 
    } 
    return ConvertSingleItem(value, newType); 
} 

public object ConvertStringToNewType(string value, Type newType) 
{ 
    // If it's not a nullable type, just pass through the parameters to Convert.ChangeType 
    if (newType.IsGenericType && newType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 
    { 
     if (value == null) 
     { 
      return null; 
     } 
     return ConvertStringToNewNonNullableType(value, new NullableConverter(newType).UnderlyingType); 
    } 
    return ConvertStringToNewNonNullableType(value, newType); 
} 

public object CallMethod(object instance, MethodInfo methodInfo, Dictionary<string, string> parameters) 
{ 
    var methodParameters = methodInfo.GetParameters(); 

    var parametersForInvocation = new List<object>(); 
    foreach (var methodParameter in methodParameters) 
    { 
     string value; 
     if (parameters.TryGetValue(methodParameter.Name, out value)) 
     { 
      var convertedValue = ConvertStringToNewType(value, methodParameter.ParameterType); 
      parametersForInvocation.Add(convertedValue); 
     } 
     else 
     { 
      // Get default value of the appropriate type or throw an exception 
      var defaultValue = Activator.CreateInstance(methodParameter.ParameterType); 
      parametersForInvocation.Add(defaultValue); 
     } 
    } 
    return methodInfo.Invoke(instance, parametersForInvocation.ToArray()); 
} 

Obsługuje prymitywnych typów, Nullables i tablice prymitywnych typów. W przypadku używania typów, które nie obsługują interfejsu IConvertible, lepiej jest zaimplementować niestandardowe konwertery dla każdego typu.

Może być napisany bardziej elegancko z Linq.

Vitaliy

2

Być może dobrym sposobem na zarządzanie "konwerterami" jest utrzymanie Dictionary<Type, IMyTypeConverter> - gdzie IMyTypeConverter ma object Convert(string value).

4

Wartość chcesz przekonwertować powinno być przedmiotem, w przeciwnym razie konwersje poza standardowymi typami nie będzie działać. Możesz łatwo przekonwertować między typami takimi jak:

object value = false; // false 
Type chType = typeof(String); // System.String 
object newValue = Convert.ChangeType(value, chType); // "false" 

To takie proste. Jeśli chcesz, metoda:

public object ConvertType(object value, Type conversionType) 
{ 
    //Check if type is Nullable 
    if (conversionType.IsGenericType && 
     conversionType.GetGenericTypeDefinition() == typeof(Nullable<>)) 
    { 
     //If the type is Nullable and the value is null 
     //Just return null 
     if (value == null) 
     { 
      return null; 
     } 

     //Type is Nullable and we have a value, override conversion type to underlying 
     //type for the Nullable to avoid exception in Convert.ChangeType 
     var nullableConverter = new NullableConverter(conversionType); 
     conversionType = nullableConverter.UnderlyingType; 
    } 

    return Convert.ChangeType(value, conversionType); 
} 
+0

jak już wspomniałem w moich komentarzach typy nullable wymagają specjalnej obsługi przypadku. Zmieniłem twoją metodę, by odzwierciedlić, jak możesz to zrobić, – Clayton

+0

@Clayton, dzięki. –

Powiązane problemy