fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
3 minut

Alternatywa dla Convert.ChangeType()


09.07.2009

Drugi raz w ciągu kilku dni przytrafiły mi się kłopoty podczas wykorzystania metody Convert.ChangeType(). Scenariusz jest bardzo prostu: mam wartość pobraną skądś-tam (baza danych, http request czy cokolwiek innego) reprezentującą znany mi typ, jednak przechowywaną w postaci stringa. Wszystko śmigało jak trzeba dopóki traktowałem w ten sposób zwykłe liczby i daty. Jakiś czas temu wpadł mi tam Guid, co skończyło się wyjątkiem InvalidCastException. Teraz z kolei to samo przytrafiło się dla TimeSpan. To samo zresztą tyczy się "typów nullowalnych".

Wystarczyło zajrzeć Reflectorem w trzewia .NETa, aby poznać tego przyczynę. Wspomniana metoda potrafi sobie poradzić tylko z kilkoma na sztywno zdefiniowanymi typami:

Kilka kliknięć dzieli nas od podejrzenia jakie to są typy; inicjalizacja tablicy je zawierającej odbywa się w statycznym konstruktorze klasy Convert:

Cóż nam pozostaje? Najpierw pomyślałem, że jedyne wyjście to rozszerzenie tej metody i stworzenie takiego oto monstera oraz rozwijanie go w razie wystąpienia kolejnych nieprzewidzianych typów:

  1:  public static object ConvertEx(object value, Type conversionType)
  2:  {
  3:  	string str = value.ToString();
  4:  
  5:  	if (conversionType == typeof(Guid))
  6:  		return new Guid(str);
  7:  	if (conversionType == typeof (TimeSpan))
  8:  		return TimeSpan.Parse(str);
  9:  
 10:  	return Convert.ChangeType(value, conversionType);
 11:  }

Chwilę potem jednak dotarło do mnie, że przecież te typy JAKOŚ muszą być dynamicznie wewnątrz .NET tworzone z napisowej reprezentacji. Chociażby zwykłe ustawienia aplikacji w pliku .config mogą mieć takie typy i klasy odpowiedzialne za parsowanie konfiguracji radzą sobie z nimi doskonale. Strzał Reflectorem w klasę ConfigurationElement, która przecież ma tą funkcjonalność, okazał się dobrym początkiem poszukiwań. Oszczędzę omawiania całej drogi, w tym przypadku ważny jest sam rezultat. Rozwiązaniem okazała się klasa TypeDescriptor, z wykorzystaniem której działające rozwiązanie wygląda tak (działa dla TimeSpan, dla Guidów, dla typów nullowalnych…):

  1:  TimeSpan result = (TimeSpan)TypeDescriptor.GetConverter(typeof (TimeSpan)).ConvertFromInvariantString(str);

Jeden problem z głowy. Oczywiście od razu przychodzi na myśl napisanie fajnej metody generycznej, ale to już każdy może sobie sam zaimplementować.

0 0 votes
Article Rating
4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
nandrew
nandrew
14 years ago

Hmm, skoro znasz typ docelowy i podajesz do na poziomie kodu (a nie odgadujesz dynamicznie) to nie prościej, szybciej, czytelniej i bardziej elegancko byłoby napisać:
“TimeSpan res = TimeSpan.Parse(str);”
(większość wbudowanych typów “prostych” ma statyczną metodę Parse)

Procent
14 years ago

Jeżeli znam typ docelowy to oczywiście że użyję [Try]Parse(). W tym przypadku na przykładzie TimeSpan demonstrowałem jedynie, że prezentowane przeze mnie rozwiązanie działa również dla niego (w przeciwieństwie do Convert.ChangeType()). Docelowe wykorzystanie to tak jak napisałeś – dynamiczna konwersja na nieznany podczas kompilacji typ.

nandrew
nandrew
14 years ago

Jeżeli tak, to co miałeś na myśli przez “napisanie fajnej metody generycznej”? Takie metody nie działają dynamicznie, tz. nie można napisać:
string s = ConvertEx<obj.GetType()> (“s”);

Procent
14 years ago

Metoda generyczna przydałaby się na przykład w klasie odpowiedzialnej za dostarczanie aplkacji konfiguracji. Wówczas wygodniej byłoby korzystać z mechanizmu:
T Get<T>(string key);
var value = Get<TimeSpan>(“SomeInterval”);
niż każdorazowo pisać metody charakterystyczne dla typu który chcemu uzyskać:
object Get(string key);
var value = TimeSpan.Parse(Get(“SomeInterval”));
Moim zdaniem pierwszy przykład jest dużo czytelniejszy i, co dość istotne, wygląda tak samo dla każdego typu. Jednocześnie obsługę stostownych wyjątków umieszczamy tylko w jednym miejscu – metodzie Get<T> – zamiast zajmować się nią każdorazowo w zależności od tego jaki typ chcemy uzyskać.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również