Почему Cast & lt; long & gt; не может быть выбрано из списка & lt; int & gt ;? [Дубликат]

Будьте предельно осторожны, используя любые другие предложения. Все зависит от контекста.

Я долго трассировал ошибки в системе, которая предположила a==b, если |a-b|<epsilon. Основными проблемами были:

  1. Неявная презумпция в алгоритме, что если a==b и b==c, то a==c.
  2. Используя тот же самый эпсилон для линий, измеренных в дюймах и линиях, измеренных в милях (0,001 дюйм). Это a==b, но 1000a!=1000b. (Вот почему AlmostEqual2sComplement запрашивает epsilon или max ULPS).
  3. Использование одного и того же эпсилона для косинуса углов и длины линий!
  4. Используя такое сравнение функция для сортировки элементов в коллекции. [В этом случае с использованием встроенного C ++-оператора == для пар нулей были получены правильные результаты.)

Как я уже сказал, все зависит от контекста и ожидаемого размера a и b .

BTW, std::numeric_limits<double>::epsilon() - это «машинный эпсилон». Это разница между 1.0 и следующим значением, представляемым двойным. Я предполагаю, что он может использоваться в функции сравнения, но только если ожидаемые значения меньше 1. (Это ответ на ответ @ cdv ...)

Кроме того, если вы в основном имеете int арифметики в doubles (здесь мы используем двойники для хранения значений int в некоторых случаях), ваша арифметика будет правильной. Например, 4.0 / 2.0 будет таким же, как 1.0 + 1.0. Это до тех пор, пока вы не делаете то, что приводит к фракциям (4.0 / 3.0) или не выходят за пределы размера int.

43
задан jason 15 January 2009 в 04:19
поделиться

6 ответов

Это очень странно! Здесь есть сообщение в блоге здесь , в котором описывается, как изменилось поведение Cast<T>() между .NET 3.5 и .NET 3.5 SP1, но оно по-прежнему не объясняет InvalidCastException, которое вы даже получаете, если переписываете ваш код таким образом:

var list = new[] { 1 };
var castedList = from long l in list select l;
Console.WriteLine(castedList.First());

Очевидно, вы можете обойти это, сделав бросок самостоятельно

var castedList = list.Select(i => (long)i);

Это работает, но это не объясняет ошибку в первую очередь , Я попытался сделать список коротким и плавающим, и те бросили то же исключение.

Изменить

В этом сообщении в блоге объясняется, почему он не работает!

Cast<T>() является методом расширения на IEnumerable, а не IEnumerable<T>. Это означает, что к тому моменту, когда каждое значение дойдет до того момента, когда оно будет запущено, оно снова помещено в объект System.Object. По сути, он пытается это сделать:

int i = 1;
object o = i;
long l = (long)o;

Этот код генерирует InvalidCastException, которое вы получаете. Если вы пытаетесь применить int непосредственно к длинному, вы в порядке, но отбрасывание коробочного int back в long не работает.

Конечно, странность!

58
ответ дан Matt Hamilton 20 August 2018 в 21:28
поделиться
  • 1
    +1 - У меня была эта точная проблема, и я читал блог и не мог это решить. Ваша правая версия очистила его. Большой! – Jeff Yates 15 September 2009 в 18:35
  • 2
  • 3
    @Timwi: Извините, но нет. См. Мой комментарий к вашему комментарию выше. – jason 13 December 2010 в 22:32
  • 4
    @ Джейсон: Извините, но да. Жаль, что вы ответите так агрессивно, когда не поймете проблему. Вы даже не обращались к моему комментарию о явных операторах преобразования. – Timwi 16 December 2010 в 03:43
  • 5
    В случае, если кто-то задается вопросом, могут ли они написать свой собственный метод Cast<T> для «fix & quot; это, см. мой недавний ответ. – Jeppe Stig Nielsen 21 November 2012 в 15:32

Конечно, разумная вещь - использовать Select(i => (long)i), и это то, что я бы рекомендовал для конверсий между встроенными типами значений и для пользовательского преобразования.

Но так же, как любопытный замечание, так как .NET 4 можно сделать свой собственный метод расширения, который также работает с этими видами конверсий. Но для этого требуется, чтобы вы использовали ключевое слово dynamic. Это происходит просто так:

public static IEnumerable<TResult> CastSuper<TResult>(this IEnumerable source)
{
    foreach (var s in source)
        yield return (TResult)(dynamic)s;
}

Как я уже говорил, работает с интегральными преобразованиями (сужение или расширение преобразований), числовые преобразования в / из / между типами с плавающей запятой и «методы» преобразования implicit operator и explicit operator.

И, конечно же, он по-прежнему работает с хорошими старыми обратными преобразованиями и распаковками, такими как оригинал System.Enumerable.Cast<TResult>.

1
ответ дан Jeppe Stig Nielsen 20 August 2018 в 21:28
поделиться
  • 1
    Ожидайте снижение производительности, если вы используете dynamic, поскольку анализ того, какое преобразование использовать, должно быть выполнено во время выполнения. Вот почему .Select(i => (long)i) рекомендуется. – Jeppe Stig Nielsen 21 November 2012 в 15:31

Метод Enumerable.Cast определяется как следующий:

public static IEnumerable<TResult> Cast<TResult>(
    this IEnumerable source
)

И нет информации об исходном типе элементов IEnumerable, поэтому я думаю, что каждый из ваших ints первоначально преобразован в System.Object через бокс и затем он пытается быть распакован в длинную переменную, и это неверно.

Аналогичный код для воспроизведения:

int i = 1;
object o = i; // boxing
long l = (long)o; // unboxing, incorrect
// long l = (int)o; // this will work

Таким образом, решение для вашей проблемы будет:

ints.Select(i => (long)i)
26
ответ дан okutane 20 August 2018 в 21:28
поделиться

Хм ... интересная головоломка. Тем более интересно, что я просто запустил его в Visual Studio 2008, и он вообще не выбрал .

Я не использую Service Pack 1, и вы можете быть , так что это может быть проблемой. Я знаю, что в выпуске SP1 были выпущены некоторые улучшения производительности в .Cast (), которые могут вызвать проблему. Некоторое чтение:

Запись в блоге 1

Запись в блоге 2

3
ответ дан Randolpho 20 August 2018 в 21:28
поделиться
  • 1
    +1 не сломал для меня ни – Andrew Hare 15 January 2009 в 04:18
  • 2
    Да, это было "исправлено" использовать трансляцию, а не конвертировать с SP1 (в соответствии с первым сообщением в блоге, с которым вы связались). – Matt Hamilton 15 January 2009 в 04:25

Вот о чем подумать ...

  1. Вы хотите сделать или преобразовать?
  2. Вы хотите, чтобы результат был как List<T> или IEnumerable<T>.
  3. Если результатом является IEnumerable<T>, вы хотите, чтобы приведение cast / convert выполнялось лениво (т. е. cast / convert фактически не произойдет до тех пор, пока итератор не достигнет каждого элемента)?

Полезное различие между cast / convert, поскольку оператор литья часто включает в себя создание нового объекта и может считаться преобразованием: реализации «Cast» должны автоматически применять операторы преобразования, определенные для задействованных типов; новый объект может быть или не быть сконструирован . Реализации «Преобразовать» должны позволять задавать делегат System.Converter<TInput,TOutput>.

Потенциальные заголовки методов:

List<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
List<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
IEnumerable<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
IEnumerable<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);

Проблемные реализации «Cast» с использованием существующей структуры; предположим, что вы передаете вход List<string>, который хотите преобразовать с любым из предыдущих методов.

//Select can return only a lazy read-only iterator; also fails to use existing explicit cast operator, because such a cast isn't possible in c# for a generic type parameter (so says VS2008)
list.Select<TInput,TOutput>( (TInput x) => (TOutput)x );
//Cast fails, unless TOutput has an explicit conversion operator defined for 'object' to 'TOutput'; this confusion is what lead to this topic in the first place
list.Cast<TOuput>();

Проблемные реализации «Преобразовать»

//Again, the cast to a generic type parameter not possible in c#; also, this requires a List<T> as input instead of just an IEnumerable<T>.
list.ConvertAll<TOutput>( new Converter<TInput,TOuput>( (TInput x) => (TOutput)x ) );
//This would be nice, except reflection is used, and must be used since c# hides the method name for explicit operators "op_Explicit", making it difficult to obtain a delegate any other way.
list.ConvertAll<TOutput>(
    (Converter<TInput,TOutput>)Delegate.CreateDelegate(
        typeof(Converter<TInput,TOutput>),
        typeof(TOutput).GetMethod( "op_Explicit", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public )
    )
);

/ Преобразование методов должно включать определенные явные операторы преобразования или позволить указать делегат преобразования. Спецификация языка C # для операторов преобразования - в частности, отсутствие имени метода - затрудняет получение делегата, за исключением отражения. Альтернативой является инкапсуляция или копирование кода конверсии, что приводит к излишней (несовместимости) сложности вашего кода, так как действительно, возможные / разрешенные преобразования являются неявными в присутствии или отсутствии операторов преобразования и должны быть обрабатывается компилятором. Нам не нужно вручную искать критически названные определения (например, «op_Explicit») соответствующих операторов преобразования с отражением в RUN TIME по соответствующим типам. Кроме того, методы Cast / Convert для конверсий с массовыми / списками с использованием явных операторов преобразования должны действительно быть функцией структуры, а с List.ConvertAll<T> они ... кроме спецификации языка затрудняют получение делегата для операторов преобразования эффективно! !!

3
ответ дан Triynko 20 August 2018 в 21:28
поделиться
  • 1
    Обратите внимание, что это не помогает вообще для таких преобразований в исходном вопросе (int to long), которые определены языком. – kvb 8 April 2009 в 05:32
  • 2
    В самом деле. Я становлюсь все более раздраженным с .net и c #, потому что есть так много способов конвертировать вещи, ни одна из которых не делает очевидного пути применения определенных явных операторов преобразования. Более того, отражение, по-видимому, является единственным способом применения явных операторов преобразования в общем случае. – Triynko 8 April 2009 в 17:38
  • 3
    В разделе «Реализация проблемных преобразований» я использую typeof (TOutput) для получения метода MethodInfo оператора преобразования, но он может быть определен на typeof (TInput) или niether, поэтому фактическая логика для этого идеального подхода будет более сложной. Все дело в том, что он не должен быть таким сложным и не выполняться во время выполнения. – Triynko 8 April 2009 в 19:42
3
ответ дан Triynko 31 October 2018 в 16:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: