21

.net フレームワーク コードの逆コンパイルされたソースを見ると、ほとんどの API には次のようなチェックがあります。

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

のようなより一般的なクラスを使用する代わりに、メソッドの引数に

Guard.IsNotNull(source);

毎回この明示的な処理を行う理由はありますか?それとも、フレームワークが開発され、新しいクラスがこれに移行して以来、これは単なるレガシー コードですか?それとも、明示的なチェックを行うことの固有の利点はありますか? 私が思いとどまることができた理由の 1 つは、おそらく、スタックが関数ポインターで過負荷になるのを避けるためです。

4

5 に答える 5

24

マシューズの回答に追加:

提案された構文はGuard.IsNotNull(source);、最初のコード スニペットと直接同等ではありません。パラメーターの値のみを渡し、その名前は渡さないため、スローされた例外は問題のあるパラメーターの名前を報告できません。パラメータの1 つが であることを知っているだけですnull

次のように式ツリーを使用することもできますが、この式ツリーを分析すると、実行時Guard.IsNotNull(() => source);のパフォーマンスにかなり大きな影響があるため、これもオプションではありません。

提案された構文は、静的ウィーバーと組み合わせてのみ使用できます。これは基本的に、生成された IL を変更するポスト コンパイラです。それが Code Contracts が使用しているアプローチです。ただし、これには次のような独自のコストが伴います。

  1. その静的ウィーバーは、そもそも誰かが書く必要があります
  2. ビルド時間が長くなります
  3. ウィーバーは、デバッグ シンボルにもパッチを適用する必要があります。
  4. エディット コンティニュであらゆる種類の問題が発生します。
于 2013-05-29T07:38:59.300 に答える
11

現在、Code Contractsこれを使用できるため、次を使用できます。

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

などですが、これはまだ CLR に組み込まれていないため (個別にダウンロードする必要があります)、独自のコードでしか実行できません。

Code Contracts クラス自体は、バージョン 4 から .Net の一部になっていますが、それ自体ではチェック コードを生成しません。そのためには、コードの生成時に C# コンパイラによって呼び出されるコード コントラクト リライターが必要です。それは別のダウンロードが必要なものです。

はい、現在これを行うためのより良い方法がありますが、CLR の一部として (まだ) リリースされていないため、CLR は現在、「レガシー」アプローチと考えられるものを使用しています。

「関数ポインターでスタックをオーバーロードする」こととはまったく関係ありません。

Code Contracts を使用しても、もちろんチェックは行っています。引数の null をチェックし、そうであればスローする IL コマンドは私が知っているものではないため、そのような作業は (すべての CLR 言語で) いくつかの IL 命令を使用して行う必要があります。ただし、Code Contracts コード リライタは、コード コントラクトの述語 (例: value != null) をチェックするためのメソッドを呼び出すのではなく、インライン コードを生成するため、非常に効率的です。

于 2013-05-29T07:09:30.110 に答える
5

.NET フレームワークには Guard クラスがないため、提案された代替手段は実行できません。フレームワークへのその後の追加は、コード コントラクトを使用しますが、控えめにします。Microsoft のすべての .NET プログラマーがコントラクトがそれほど有用であると確信しているわけではないようですが、私も同じ意見です。

それ以外の場合は、Microsoft の仕組みが表示されます。.NET Framework のコードは、社内の多くの小さなチームによって貢献されています。典型的なチームのサイズは、約 10 人のプログラマーです。そうでなければ、ソフトウェア開発ビジネスの誰もが知っていることに同意します。大規模なチームは機能しません。全員のコミュニケーションに費やされる時間が、実際にコードを生成するために費やされる時間の量を圧倒し始めるクリティカル マスがあります。

そのようなチームも常に作成され、解散されます。フレームワークの多くの部分には、それを維持するアクティブなチームがありません。通常は、重要なセキュリティ アップデートや、必要に応じてバグ修正を提供できるほど内部を熟知している担当者が 1 人だけです。そのような解散したチームが書いたコードは、非常に保守モードにあり、変更は絶対に必要な場合にのみ行われます。マイナーなスタイルの変更を行うメリットがないという理由だけでなく、重大な変更が無意識のうちに追加される可能性を減らすためです。

これは .NET フレームワークの責任です。そのコードがプライベート メソッド内に存在する場合でも、外部から見えるようにするためのコツを持っている内部がたくさんあります。例外のように。また、リフレクションを使用してフレームワークの制限を回避するプログラマー。そして、非常に微妙な問題です。良い例は、インターンによって作成された、Microsoft 内で広く使用されている電子メール アプリのバグです。マシンを .NET 1.1 から .NET 2.0 に更新したときにクラッシュし、全員が電子メールを受信できなくなりました。その電子メール アプリのバグは、.NET 1.1 で実行したときにトリガーされなかった潜在的なスレッド競合でした。しかし、.NET 2.0 フレームワーク コードのタイミングがわずかに変更されたことで明らかになりました。

于 2013-05-29T09:35:41.527 に答える
3

これは .NET Framework の一部ではないかもしれませんが、Microsoft の開発者はこの概念を採用しているようです (コード コントラクトの代わりに JetBrains 注釈を使用していることにも注意してください)。

https://github.com/aspnet/EntityFramework/blob/master/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;
        }
...
于 2014-05-23T09:32:26.390 に答える
0

私が考えることができる唯一のことは、Guardクラスがある場合、例外スタックトレースでは、Guard実際に呼び出されたメソッドに問題があるかのように見えるということGuardです。キャッチして再スローすることでこれを回避できますが、本番コードにボイラープレートが再び含まれています。

于 2013-05-29T09:44:00.563 に答える