Почему инфраструктура .Net использует класс Guard (или эквивалент) для аргументов метода

если посмотреть на декомпилированный источник кода .net framework, большинство API-интерфейсов имеют такие проверки, как эти

if (source == null)
    throw Error.ArgumentNull("source");

в аргументах метода вместо использования более общего класса, такого как

Guard.IsNotNull(source);

Есть ли причина в том, чтобы делать это каждый раз, или это просто устаревший код, который был вокруг с момента разработки структуры, и новые классы продвигаются к этому или есть какие-либо неотъемлемые преимущества наличия явных проверок? Одна из причин, по которой я мог бы подумать, - это, вероятно, избежать перегрузки стека указателями на функции.

Ответ 1

Добавление в ответ Мэтьюза:

Ваш предложенный синтаксис Guard.IsNotNull(source); не является прямым эквивалентом первого фрагмента кода. Он передает только значение параметра, но не его имя, поэтому заброшенное исключение не может сообщить имя параметра-нарушителя. Он просто знает, что один из параметров null.

Вы можете использовать деревья выражений - например: Guard.IsNotNull(() => source); - но анализ этого дерева выражений имеет довольно большое влияние производительности во время выполнения, поэтому это тоже не вариант.

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

  • Этот статический ткач должен быть написан кем-то в первую очередь
  • Увеличивает время сборки
  • Ткачи также должны исправлять символы отладки
  • Он вызывает всевозможные проблемы с Edit и Continue

Ответ 2

В настоящее время мы можем сделать это с помощью Code Contracts, чтобы мы могли использовать:

Contract.Requires(source != null);
Contract.Ensures(Contract.Result<MyType>() != null);

и т.д., но в настоящее время мы можем сделать это только в нашем собственном коде, потому что это еще не встроено в CLR (это отдельная загрузка).

Сами классы Code Contracts являются частью .NET с версии 4, но сами по себе они не генерируют никакого кода проверки. Для этого нам нужен перезаписывающий код, который будет вызываться компилятором С# при генерации кода. Это то, что требует отдельной загрузки.

Итак, да, у нас есть лучшие способы сделать это сейчас, но он еще не был выпущен как часть CLR (пока), и поэтому CLR в настоящее время использует то, что вы считаете "наследием".

Это, безусловно, не имеет никакого отношения к "перегрузке стека указателями на функции".

Даже с кодовыми контрактами мы все еще проводим проверку. Там нет команды IL, которую я знаю об этом, проверяет аргумент для null и throw, если это так, поэтому такую ​​работу нужно выполнить с помощью нескольких инструкций IL (на всех языках CLR). Однако код-код-код переписывает код встроенного кода для проверки предиката кода контракта (например, value != null) вместо вызова метода для этого, поэтому он очень эффективен.

Ответ 3

В .NET Framework нет класса Guard, поэтому предлагаемая вами альтернатива не представляется возможной. Более поздние дополнения к структуре используют кодовые контракты, но довольно экономно. Не каждый программист .NET в Microsoft кажется убежденным в том, что контракты полезны, я разделяю настроения.

В противном случае вы увидите, как работает Microsoft. Код в .NET Framework представлен множеством небольших команд внутри компании. Типичный размер команды - около 10 программистов. В противном случае, кивком, которого знают все в бизнесе разработчиков программного обеспечения, большие команды не работают. Там критическая масса, где количество времени, затрачиваемого на то, чтобы заставить всех общаться, начинает подавлять количество времени, которое может быть потрачено на фактическое получение кода.

Такие команды также постоянно создаются и распускаются. У многих частей рамки больше нет активной команды, которая ее поддерживает. Обычно только один парень, который до сих пор хорошо знает внутренние компоненты, чтобы обеспечить критические обновления для системы безопасности и, возможно, исправлять ошибки, когда это необходимо. Код, который написал такая расформированная команда, очень много в режиме обслуживания, изменения производятся только в случае крайней необходимости. Не только потому, что нет никакой пользы для внесения незначительных стилистических изменений, а для уменьшения вероятности того, что изменения в изменении неясно добавлены.

Что является ответственностью за платформу .NET, существует множество внутренних элементов, которые умеют быть внешне видимыми, даже если этот код живет внутри частных методов. Как исключения. И программисты, использующие Reflection для взлома рамки ограничений. И действительно тонкий материал, отличный пример - ошибка в приложении электронной почты, широко используемом внутри Microsoft, написанном стажером. Который разбился и оставил всех без электронной почты, когда они обновили свою машину с .NET 1.1 до .NET 2.0. Ошибка в этом приложении электронной почты была скрытой нитью, которая никогда не срабатывала при работе с .NET 1.1. Но стал заметен из-за очень незначительного изменения сроков разработки кода платформы .NET 2.0.

Ответ 4

Это может быть не часть .NET Framework, но разработчики Microsoft, похоже, используют концепцию (обратите внимание на использование аннотаций JetBrains вместо кодовых контрактов):

https://github.com/aspnet/EntityFramework/blob/dev/src/Shared/Check.cs

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;

namespace Microsoft.Data.Entity.Utilities
{
    [DebuggerStepThrough]
    internal static class Check
    {
        [ContractAnnotation("value:null => halt")]
        public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
        {
            NotEmpty(parameterName, "parameterName");

            if (ReferenceEquals(value, null))
            {
                throw new ArgumentNullException(parameterName);
            }

            return value;
        }
...

Ответ 5

Единственное, о чем я могу думать, это то, что если у вас есть класс Guard, то в исключении stacktrace будет выглядеть так, как если бы проблема была в Guard, когда она фактически находилась в методе, который назывался Guard. Вы можете обойти это, поймав и перевернув, но затем вы снова получите шаблон в своем производственном коде.