Immaginate il seguente codice semplice:Come trasmettere un valore di tipo generico T al doppio senza box?
public void F<T>(IList<T> values) where T : struct
{
foreach (T value in values)
{
double result;
if (TryConvertToDouble((object)value, out result))
{
ConsumeValue(result);
}
}
}
public void ConsumeValue(double value)
{
}
Il problema con il codice di cui sopra è colata di opporsi, che si traduce nel pugilato nel ciclo.
Esiste un modo per ottenere la stessa funzionalità, ovvero alimentare ConsumeValue con tutti i valori senza ricorrere al pugilato nel ciclo foreach? Nota, che F deve essere un metodo generico.
Posso vivere con un codice di preparazione costoso purché venga eseguito all'esterno del ciclo una sola volta. Ad esempio, se è necessario emettere un metodo dinamico elegante, allora va bene se fatto una sola volta.
EDIT
T è garantito per essere di qualche tipo numerico o bool.
Motivazione. Immagina un'applicazione basata sui meta dati, in cui un agente segnala un flusso di dati, in cui il tipo di elemento di dati viene emesso dinamicamente in base ai metadati del flusso di dati. Immaginate anche che ci sia un motore normalizzatore, che sa normalizzare i flussi di dati numerici secondo alcuni algoritmi. Il tipo del flusso di dati numerico in entrata è noto solo in fase di esecuzione e può essere indirizzato a un metodo generico di quel tipo di dati. Il normalizzatore, tuttavia, si aspetta il doppio e produce il doppio. Questa è una descrizione di altissimo livello, per favore non approfondirla.
EDIT2
riguardante l'getto di raddoppiare. In realtà abbiamo un metodo per convertire a raddoppiare con la seguente firma:
bool TryConvertToDouble(object value, out double result);
avrei usato nell'esempio, in primo luogo, ma ho voluto risparmiare spazio e qualcosa di scritto che non è andare a lavorare. Risolto adesso Grazie per averlo notato
Edit3
ragazzi, l'implementazione corrente inscatolare i valori. E anche se non ho il verdetto del profiler per quanto riguarda la penalizzazione delle prestazioni (se esiste), sono comunque interessante sapere se esiste una soluzione senza boxing (e senza conversione in stringa). Lasciatemelo definire un interesse puramente accademico. Questo mi interessa davvero, perché cose del genere sono banali in C++ con i modelli, ma, naturalmente, non sto iniziando un'altra discussione stupida e inutile su ciò che è meglio dei generici .NET o dei modelli C++. Per favore, ignora quest'ultima frase.
edit4
Grazie a https://stackoverflow.com/users/267/lasse-v-karlsen che ha fornito la risposta. A dire il vero, ho usato il suo codice di esempio per scrivere una classe semplice come questo:
public static class Utils<T>
{
private static class ToDoubleConverterHolder
{
internal static Func<T, double> Value = EmitConverter();
private static Func<T, double> EmitConverter()
{
ThrowIfNotConvertableToDouble(typeof(T));
var method = new DynamicMethod(string.Empty, typeof(double), TypeArray<T>.Value);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
if (typeof(T) != typeof(double))
{
il.Emit(OpCodes.Conv_R8);
}
il.Emit(OpCodes.Ret);
return (Func<T, double>)method.CreateDelegate(typeof(Func<T, double>));
}
}
public static double ConvertToDouble(T value)
{
return ToDoubleConverterHolder.Value(value);
}
}
Dove:
- ThrowIfNotConvertableToDouble (Type) è un metodo semplice che consente di verificare il tipo di dato può essere convertito in doppio , cioè qualche tipo numerico o bool.
- TypeArray è una classe di supporto per produrre
new[]{ typeof(T) }
Procedimento Utils.ConvertToDouble converte qualsiasi valore numerico di raddoppiare nel modo più efficiente, dimostra la risposta a questa domanda.
Funziona come un incantesimo - grazie amico.
Il problema di quanto sopra è anche che non ha molto senso. Perché utilizzare un metodo generico, con un vincolo, e quindi eseguirlo con un cast rigido? Puoi spiegare più chiaramente cosa stai cercando di ottenere? –
Questo mi sembra strano. Perché stai lanciando una struttura generica a un oggetto e poi a un doppio? C'è qualcosa di sbagliato in questo esempio? Abbiamo bisogno di più contesto? Questo codice sembra così fuori luogo, che non so come rispondere ... –
Ho aggiornato la domanda. – mark