Представьте себе следующий простой код:
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)
{
}
Проблема с приведенным выше кодом заключается в том, что он бросает объект, что приводит к боксу в цикле.
Есть ли способ достичь той же функциональности, то есть загружать ConsumeValue со всеми значениями, не прибегая к боксу в цикле foreach? Обратите внимание, что F должен быть общим методом.
Я могу жить с дорогим кодом подготовки, если он выполняется за пределами цикла только один раз. Например, если требуется искушать динамический метод, то это нормально, если сделать только один раз.
ИЗМЕНИТЬ
T гарантированно имеет некоторый числовой тип или bool.
Мотивация. Представьте себе приложение, управляемое метаданными, где агент сообщает поток данных, где тип элемента данных динамически испускается на основе метаданных потока данных. Представьте себе, что есть механизм нормализатора, который знает нормализовать числовые потоки данных в соответствии с некоторым алгоритмом. Тип входящего числового потока данных известен только во время выполнения и может быть направлен на общий метод этого типа данных. Однако нормализатор ожидает удвоения и производит удвоение. Это описание очень высокого уровня, пожалуйста, не вникайте в него.
EDIT2
Отнесение к двойному. На самом деле у нас есть способ преобразования в double со следующей сигнатурой:
bool TryConvertToDouble(object value, out double result);
Я должен был использовать его в примере в первую очередь, но я хотел сэкономить место и написал что-то, что не сработает. Исправлено. Спасибо, что заметили.
EDIT3
Ребята, текущая реализация блокирует значения. И даже если у меня нет вердикта профайлера относительно его исполнения (если есть), мне все же интересно узнать, есть ли решение без бокса (и без преобразования в строку). Позвольте мне называть это чисто академическим интересом. Это действительно меня интересует, потому что подобные вещи тривиальны в С++ с шаблонами, но, конечно, я не начинаю еще один глупый и бессмысленный аргумент в отношении того, что лучше .NET-дженериков или шаблонов С++. Пожалуйста, проигнорируйте это последнее предложение.
EDIT4
Благодаря https://stackoverflow.com/users/267/lasse-v-karlsen, который предоставил ответ. На самом деле, я использовал свой пример кода для написания простого класса следующим образом:
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);
}
}
Где:
- ThrowIfNotConvertableToDouble (Тип) - это простой метод, который гарантирует, что данный тип может быть преобразован в двойной, то есть некоторый числовой тип или bool.
- TypeArray - это вспомогательный класс для создания
new[]{ typeof(T) }
Метод Utils.ConvertToDouble преобразует любое числовое значение, чтобы удвоить наиболее эффективный способ, показанный ответом на этот вопрос.
Это работает как шарм - спасибо человеку.