web-dev-qa-db-ja.com

nullパラメータをチェックする最良の方法(Guard Clauses)

たとえば、通常、コンストラクターのパラメーターをnullにしたくないため、次のようなものが表示されるのは非常に普通です

if (someArg == null)
{
    throw new ArgumentNullException(nameof(someArg));
}

if (otherArg == null)
{
    throw new ArgumentNullException(nameof(otherArg));
}

コードが少し乱雑になります

これよりも優れた引数リストの引数をチェックする方法はありますか?

「すべての引数をチェックし、引数のいずれかがnullであり、nullだった引数を提供する場合はArgumentNullExceptionをスローします。

ちなみに、重複した質問のクレームに関しては、これは属性や組み込みの何かで引数をマークすることではなく、オブジェクトが初期化された依存関係を受け取ることを保証するためにGuard Clausesと呼ぶものです。

17
SuperJMN
_public static class Ensure
{
    /// <summary>
    /// Ensures that the specified argument is not null.
    /// </summary>
    /// <param name="argumentName">Name of the argument.</param>
    /// <param name="argument">The argument.</param>
    [DebuggerStepThrough]
    [ContractAnnotation("halt <= argument:null")]        
    public static void ArgumentNotNull(object argument, [InvokerParameterName] string argumentName)
    {
        if (argument == null)
        {
            throw new ArgumentNullException(argumentName);
        }
    }
}
_

使用法:

_// C# < 6
public Constructor([NotNull] object foo)
{
    Ensure.ArgumentNotNull(foo, "foo");
    ...
}

// C# >= 6
public Constructor([NotNull] object bar)
{
    Ensure.ArgumentNotNull(bar, nameof(bar));
    ...
}
_

DebuggerStepThroughAttributeは非常に便利なため、デバッグ中に例外が発生した場合(または例外が発生した後にデバッガーをアタッチした場合)、ArgumentNotNullメソッド内ではなく、代わりにnull参照actuallyが発生したメソッドの呼び出し。

ReSharper Contract Annotations を使用しています。

  • ContractAnnotationAttributeは、引数(_"foo"_)のスペルを間違えないようにし、fooシンボルの名前を変更すると、自動的に引数の名前も変更します。
  • NotNullAttributeは、ReSharperのコード分析を支援します。したがって、new Constructor(null) ifを実行すると、ReSharperから例外が発生するという警告が表示されます。
  • コードに直接注釈を付けたくない場合は、 external XML-files を使用して同じことを行うこともできます。これは、ライブラリで展開でき、ユーザーはReShaprerで最適に参照できます。
18
bitbonk

コンストラクターにパラメーターが多すぎる場合は、それらを修正する方が良いでしょうが、それは別の話です。

定型的な検証コードを減らすために、多くの人が次のようなGuardユーティリティクラスを作成します。

public static class Guard
{
    public static void ThrowIfNull(object argumentValue, string argumentName)
    {
        if (argumentValue == null)
        {
            throw new ArgumentNullException(argumentName);
        }
    }

    // other validation methods
}

(そのGuardクラスに必要な他の検証メソッドを追加できます)。

したがって、パラメーターを検証するのに必要なコードは1行だけです。

    private static void Foo(object obj)
    {
        Guard.ThrowIfNull(obj, "obj");
    }
13
serge.karalenka

ヌル参照は、あなたが警戒しなければならないトラブルの一種です。しかし、彼らだけではありません。問題はそれよりも広範であり、これは要約すると、メソッドは特定のタイプのインスタンスを受け入れますが、すべてのインスタンスを処理することはできません。

つまり、メソッドのドメインは、処理する値のセットよりも大きくなります。次に、ガード句を使用して、実際のパラメーターがメソッドのドメインの「グレーゾーン」に該当せず、処理できないことを表明します。

現在、許容される値のセットの外側にある値として、null参照があります。一方、セットのnull以外の要素も受け入れられないことがよくあります(空の文字列など)。

その場合、メソッドのシグネチャが広すぎることが判明する場合があり、これは設計上の問題を示しています。それは再設計につながる可能性がありますサブタイプ(通常は派生インターフェース)を定義します。これにより、メソッドのドメインが制限され、ガード句の一部が非表示になります。この記事で例を見つけることができます: なぜ保護条項が必要なのですか?

6
Zoran Horvat

ガード機能を提供する Heleonix.Guard ライブラリを試してみてください。

以下のようなガード句を記述できます。

// C# 7.2+: Non-Trailing named arguments
Throw.ArgumentNullException(when: param.IsNull(), nameof(param));

// OR

// Prior to C# 7.2: You can use a helper method 'When'
Throw.ArgumentNullException(When(param.IsNull()), nameof(param));

// OR
Throw.ArgumentNullException(param == null, nameof(param));

// OR
Throw.ArgumentNullException(When (param == null), nameof(param));

多くの既存の例外のスローを提供し、カスタム例外のカスタム拡張メソッドを作成できます。また、ライブラリは、IsNullIsNullOrEmptyOrWhitespaceIsLessThanなどの述語拡張子を持つ 'Heleonix.Extensions'ライブラリを参照して、目的の値に対して引数または変数をチェックします。流れるようなインターフェイスを備えた他のいくつかのガードライブラリとは異なり、これらの拡張機能は中間オブジェクトを生成せず、実装は本当に簡単なので、パフォーマンスが向上します。

1

SwissKnife と呼ばれるnugetパッケージがあります。 nuget gallery からSwissKnifeをインストールします。 _SwissKnife.Diagnostics.Contracts_名前空間の下の引数Argument.IsNotNullOrEmpty(args,"args")のnullチェックで始まる多くのオプションとともに、オプションidoimなどがあります。 _Option<Class_Name> _someVar_を設定してから、__someVar.IsSome_または__someVar.IsNone_かどうかを確認できます。これは、null許容クラスに対しても役立ちます。お役に立てれば。