2013-08-09 11 views
7

Moje pytanie dotyczy sprawdzania typu w łańcuchu metod ogólnych. Załóżmy, że mam metodę rozszerzenia, która próbuje przekonwertować tablicę bajtów na int, decimal, string lub DateTime.Unikaj nadmiernego sprawdzania typów w metodach ogólnych?

public static T Read<T>(this ByteContainer ba, int size, string format) where T : struct, IConvertible 
{ 
    var s = string.Concat(ba.Bytes.Select(b => b.ToString(format)).ToArray());    
    var magic = FromString<T>(s); 
    return (T)Convert.ChangeType(magic, typeof(T)); 
} 

Wywołuje metodę o nazwie FromString, która tłumaczy połączony ciąg na określony typ. Niestety, logika biznesowa jest całkowicie zależny od typu T. więc skończyć z megalitycznych if-else bloku:

private static T FromString<T>(string s) where T : struct 
{ 
    if (typeof(T).Equals(typeof(decimal))) 
    { 
     var x = (decimal)System.Convert.ToInt32(s)/100; 
     return (T)Convert.ChangeType(x, typeof(T)); 
    } 
    if (typeof(T).Equals(typeof(int))) 
    { 
     var x = System.Convert.ToInt32(s); 
     return (T)Convert.ChangeType(x, typeof(T)); 
    } 
    if (typeof(T).Equals(typeof(DateTime))) 
     ... etc ... 
} 

W tym momencie, wolałbym wiele metod o tej samej nazwie i różnych rodzajów powrotów coś na wzór tego:

// <WishfulThinking> 
private static decimal FromString<T>(string s) 
{ 
    return (decimal)System.Convert.ToInt32(s)/100; 
}  
private static int FromString<T>(string s) 
{ 
    return System.Convert.ToInt32(s); 
} 
// </WishfulThinking> 

... ale zdaję sobie sprawę, to nie jest ważne, jak T nie może być ograniczone do określonego typu, a bez niego, wszystkie metody będą miały taką samą sprzecznych podpis.

Czy istnieje możliwy sposób wdrożenia programu FromString bez nadmiernego sprawdzania typów? A może istnieje lepszy sposób na całkowite rozwiązanie tego problemu?

+0

To wydaje się problemu możesz rozwiązać za pomocą Wyrażeń. – lukegravitt

Odpowiedz

10

Albo może być lepszy sposób podejścia do tego problemu w ogóle?

Jasne jest jedno: można zrobić każdy konwerter do lambda, zrób słownika nich i używać ich do nawrócenia, tak:

private static IDictionary<Type,Func<string,object>> Converters = new Dictionary<Type,Func<string,object>> { 
    {typeof(int), s => Convert.ChangeType(System.Convert.ToInt32(s), typeof(int))} 
, {typeof(decimal), s => Convert.ChangeType((decimal)System.Convert.ToInt32(s)/100, typeof(decimal))} 
, ... // And so on 
}; 
public static T Read<T>(this ByteContainer ba, int size, string format) where T : struct, IConvertible { 
    Func<string,object> converter; 
    if (!Converters.TryGetValue(typeof(T), out converter)) { 
     throw new ArgumentException("Unsupported type: "+typeof(T)); 
    } 
    var s = string.Concat(ba.Bytes.Select(b => b.ToString(format)).ToArray()); 
    return (T)converter(s); 
} 
+2

Myślę, że to podejście tylko rozwiało mój umysł. Będę musiał spędzić trochę czasu z tym ... – MadHenchbot

+0

Zamiast używać statycznego słownika, jak o użyciu prywatnej ogólnej klasy statycznej 'MyConverters ' z polem typu 'Func ', który domyślnie jest metodą który sprawdzi, czy 'T' jest znanym typem i czy zgłosi wyjątek lub zaktualizuje pole odpowiednio? Następnie 'Read ' może wywołać 'MyConverters .Converter' bez jakiejkolwiek logiki warunkowej potrzebnej po pierwszym użyciu. – supercat

+0

@dasblinkenlight Dzięki za wystawienie mnie na ten pomysł!Twoje rozwiązanie rozwiązało mój problem i sprawiło, że zacząłem myśleć o C# w nowy i ekscytujący sposób. Bardzo doceniane. – MadHenchbot

6

Ogólnie, jeśli trzeba pisać logikę, która musi zawsze sprawdzać typ ogólnego parametru typu, w rzeczywistości nie pisze się kodu, który czerpie korzyści z bycia generycznym. W takim przypadku i zakładając, że faktycznym problemem, który próbujesz rozwiązać, jest potrzeba przekonwertowania tablicy bajtów na przewidywalny wbudowany typ, który reprezentuje, zalecam porzucić to podejście i użyć metod z BitConverter class.

W punkcie, w którym można określić wartość T, po prostu wywołaj odpowiednią metodę na klasie BitConverter.

Aktualizacja: Jeśli ogólny rozwiązanie jest konieczne, sugeruję coś podobnego do dasblinkenlight „s odpowiedź, chociaż chciałbym niech rozmówca wstrzyknąć konwerter (wszakże rozmówca wie wymagany typ wynik) , co pozwala uniknąć problemu prowadzenia listy funkcji konwersji obok metody rodzajowe:

public static T Read<T>(this ByteContainer ba, int size, string format, 
         Func<string, T> converter) where T : struct, IConvertible 
{ 
    var s = string.Concat(ba.Bytes.Select(b => b.ToString(format)).ToArray()); 
    return converter(s); 
} 
+0

Zdecydowanie się z tobą zgadzam. Niestety, z konwersją wiąże się tak wiele procesów tłumaczeniowych, że nie jestem pewien, czy BitConverter wystarczy w moim przypadku. – MadHenchbot

Powiązane problemy