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

Я просто пересматриваю четвертую главу C # in Depth, которая посвящена обнуляемым типам, и добавляю раздел об использовании оператора «as», который позволяет писать:

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

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

Однако, похоже, это не так. Я включил пример тестового приложения ниже, которое в основном суммирует все целые числа в массиве объектов - но массив содержит множество пустых ссылок и ссылок на строки, а также целочисленные значения в штучной упаковке. Тест измеряет код, который вы должны использовать в 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<int>().Sum();
        sw.Stop();
        Console.WriteLine("LINQ: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }
}

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

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