Нулевая проверка на типах ссылки и значения - PullRequest
0 голосов
/ 05 октября 2011

(Обновление - из комментариев) Вопрос: есть ли преимущество использования одного метода расширения над другим?

Из обсуждения, которое я веду в моей статье codeproject. в методах расширения, я не уверен, правильно ли следующее:

В настоящее время у меня есть следующий метод расширения:

public static bool In<T>(this T source, params T[] list)
{
    if (null == source) throw new ArgumentNullException("source");
    return list.Contains(source);
}

, который работает, как ожидалось.В комментариях было предложено изменить его так, чтобы он проверял только ссылочные типы следующим образом:

public static bool In<T>(this T source, params T[] list)
{
     if (!typeof(T).IsValueType)
     {
         if (Equals(source, default(T))) throw new ArgumentNullException("source");
     }
     return list.Contains(source);
}

Опять же, это работает, как и ожидалось. Есть ли какое-либо преимущество второго метода перед первым , учитывая, что при быстром тестировании мы говорим о 0,001 второй разности для 10000 прогонов.

Выход теста (Core i3 @4 ГГц, RAID 0 ssd):

Testing performance...

Value type, original: 00:00:00.0033289
Value type, from code project: 00:00:00.0033027
Reference type, original: 00:00:00.0076951
Reference type, from code project: 00:00:00.0068459

Код теста:

        Console.WriteLine("Testing performance...");
        Console.WriteLine("");

        const Int32 _runs = 10000;

        Stopwatch sw = new Stopwatch();
        Console.Write("Value type, original: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                i.In(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());

        sw = new Stopwatch();
        Console.Write("Value type, from code project: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                i.In2(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());


        sw = new Stopwatch();
        Console.Write("Reference type, original: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                "This String".In("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());

        sw = new Stopwatch();
        Console.Write("Reference type, from code project: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                "This String".In("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());

        Console.WriteLine("");
        Console.ReadLine();


public static bool In<T>(this T source, params T[] list)
{
    if (source == null) throw new ArgumentNullException("source");
    return list.Contains(source);
}

public static bool In2<T>(this T source, params T[] list)
{
    if (!typeof(T).IsValueType)
    {
        if (Equals(source, default(T))) throw new ArgumentNullException("source");
    }
    return list.Contains(source);
}

Ответы [ 3 ]

2 голосов
/ 05 октября 2011

Я бы оставил ваш код как

public static bool In<T>(this T source, params T[] list)
{
    if (null == source) throw new ArgumentNullException("source");
    return list.Contains(source);
}

потому что его легче читать.

Относительно примечания: может ли источник быть типом значения? Если нет, вы можете ограничить T как T:class.

1 голос
/ 05 октября 2011

Помимо улучшения производительности, которое является спорным, так как делает код менее читабельным (но не слишком), меня больше беспокоит тот факт, что ваши два метода не имеют одинаковую семантику. Есть действительно типы значений, которые могут быть нулевыми: Nullable<TValue>, более известный как TValue?.

Следующий код:

int? nullableInt = null;
nullableInt.In(list);

создает исключение ArgumentNullException в первой реализации, а не во второй (при условии, что список был предварительно правильно инициализирован).

1 голос
/ 05 октября 2011

Два метода в основном эквивалентны.

В исходной версии, если T является типом значения, тогда тест всегда завершается неудачей: тип значения никогда не равен нулевому указателю.Поскольку условие всегда ложно, тест оптимизируется.

Во второй версии тест выполняется явно, но результат точно такой же.

Не вижу смысла предпочитатьодин над другим.Ваш оригинал, вероятно, немного быстрее для типов значений, но я бы добавил комментарий, объясняющий, почему он работает.

...