Сюрприз производительности с «как» и обнуляемыми типами

я просто пересматриваю главу 4 C # в глубине, которая имеет дело с обнуляемыми типами, и ядобавив раздел об использовании "как" оператор, который позволяет написать:

object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    ... // Use x.Value in here
}

Я думал, что это действительно здорово, и что это может улучшить производительность по сравнению с эквивалентом C # 1, используя "является" с последующим приведением - в конце концов, таким образом, нам нужно только один раз запросить динамическую проверку типа, а затем простую проверку значения.

Однако, похоже, это не так. Я'ниже мы включили пример тестового приложения, которое в основном суммирует все целые числа в массиве объектов, но массив содержит множество пустых ссылок и ссылок на строки, а также целочисленные значения в штучной упаковке. Тест измеряет код, который выЯ должен использовать в C # 1, код с использованием "как" оператор, и просто для пинков решение LINQ. К моему удивлению, код C # 1 в этом случае работает в 20 раз быстрее - и даже код LINQ (который яя ожидал, что будет медленнее, учитывая участие итераторов)как" код.

Является ли реализация .NETisinst для обнуляемых типов просто очень медленно? Это дополнительныйunbox.any что вызывает проблему? Есть ли другое объяснение этому? Сейчас я чувствую, что ямне придется включить предупреждение против использования этого в ситуациях, чувствительных к производительности ...

Результаты:

В ролях: 10000000: 121

As: 10000000: 2211

LINQ: 10000000: 2143

Код:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i+1] = "";
            values[i+2] = 1;
        }

        FindSumWithCast(values);
        FindSumWithAs(values);
        FindSumWithLinq(values);
    }

    static void FindSumWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int) o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithLinq(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = values.OfType().Sum();
        sw.Stop();
        Console.WriteLine("LINQ: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }
}

Ответы на вопрос(10)

Ваш ответ на вопрос