25

どうやら、コードのエラーの大部分はnull参照の例外です。null参照エラーの発生を回避するための一般的な手法はありますか?

間違えない限り、F#などの言語ではnull値を設定できないことを認識しています。しかし、それは問題ではありません。C#などの言語でnull参照エラーを回避する方法を尋ねています。

4

16 に答える 16

32

null参照例外がユーザーに表示される場合、これは開発者側のエラーに起因するコードの欠陥を示しています。これらのエラーを防ぐ方法に関するいくつかのアイデアがあります。

ソフトウェアの品質に関心があり、.netプログラミングプラットフォームも使用している人への私の一番の推奨事項は、Microsoftコードコントラクト(http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx)をインストールして使用することです。 。実行時チェックと静的検証を行う機能が含まれています。これらのコントラクトをコードに組み込むための基本的な機能は、4.0バージョンの.netフレームワークに含まれています。コードの品質に関心があり、そのように聞こえる場合は、Microsoftコードコントラクトの使用を本当に楽しむことができます。

Microsoftコードコントラクトでは、「Contract.Requires(customer!= null);」のような前提条件を追加することで、メソッドをnull値から保護できます。このような前提条件を追加することは、上記のコメントで他の多くの人が推奨する方法と同等です。コード契約の前に、私はあなたがこのようなことをすることを勧めたでしょう

if (customer == null) {throw new ArgumentNullException("customer");}

今私はお勧めします

Contract.Requires(customer != null);

次に、これらの欠陥をできるだけ早くキャッ​​チするランタイムチェックシステムを有効にして、欠陥コードの診断と修正に導きます。ただし、コードコントラクトは、引数のnull例外を置き換えるための単なる空想的な方法であるという印象を与えないでください。それらはそれよりもはるかに強力です。Microsoftコードコントラクトを使用すると、静的チェッカーを実行して、null参照例外が発生する可能性のあるコード内の可能性のあるサイトを調査するように依頼することもできます。静的チェッカーを簡単に使用するには、もう少し経験が必要です。初心者にはお勧めしません。しかし、気軽に試してみてください。

ヌル参照エラーの蔓延に関する研究

このスレッドでは、null参照エラーが重大な問題であるかどうかについていくつかの議論がありました。長い答えは以下のとおりです。それを乗り越えたくない人のために、要約します。

  • Spec#およびコード契約プロジェクトのプログラムの正当性に関するMicrosoftの主要な研究者は、これは対処する価値のある問題であると考えています。
  • Eiffelプログラミング言語を開発およびサポートしたBertrandMeyer博士とISEのソフトウェアエンジニアチームも、これは対処する価値のある問題であると考えています。
  • 通常のソフトウェアを開発している私自身の商業経験では、null参照エラーを何度も目にしているので、自分の製品や手法の問題に対処したいと思います。

マイクロソフトは何年もの間、ソフトウェアの品質を向上させるために設計された研究に投資してきました。彼らの取り組みの1つは、Spec#プロジェクトでした。.net 4.0フレームワークに関する私の意見で最もエキサイティングな開発の1つは、Microsoftコードコントラクトの導入です。これは、Spec#研究チームによって行われた以前の作業の成果です。

「コード内のエラーの大部分はnull参照例外です」というあなたの発言に関して、いくつかの不一致を引き起こすのは修飾子「大多数」であると私は信じています。「大多数」というフレーズは、おそらく70〜90%の障害が根本原因としてnull参照例外を持っていることを示唆しています。これは私には高すぎるようです。Microsoft Spec#の調査から引用したいと思います。彼らの記事の中で、Spec#プログラミングシステム:概要、Mike Barnett、K。Rustan M. Leino、およびWolframSchulteによる。CASSIS 2004では、LNCSvol。3362、Springer、2004、彼らは書いた

1.0非ヌル型最近のプログラムの多くのエラーは、ヌル逆参照エラーとして現れます。これは、ヌルと評価される可能性のある式とそうでない式を区別する機能を提供するプログラミング言語の重要性を示唆しています(いくつかの実験的証拠については、 [24、22]を参照)。実際、すべてのnull逆参照エラーを根絶したいと考えています。

これは、この調査に精通しているMicrosoftの人々にとっておそらく情報源です。この記事はSpec#サイトで入手できます。

以下の参考文献22と24をコピーし、便宜上ISBNを含めました。

  • マニュエルファーンドリッヒとK.ルスタンM.レイノ。オブジェクト指向言語でのnull以外の型の宣言とチェック。オブジェクト指向プログラミング、システム、言語、およびアプリケーションに関する2003 ACM会議の議事録、OOPSLA 2003、第38巻、SIGPLAN通知の第11号、302〜312ページ。ACM、2003年11月。isbn= {1-58113-712-5}、

  • コーマックフラナガン、K。ルスタンM.レイノ、マークリリブリッジ、グレッグネルソン、ジェームスB.サックス、レイミースタタ。Javaの拡張静的チェック。プログラミング言語の設計と実装に関する2002ACMSIGPLAN会議(PLDI)の議事録、第37巻、SIGPLAN通知の第5号、234〜245ページ。ACM、2002年5月。

これらの参考資料を確認しました。最初の参照は、ヌル参照の欠陥の可能性について独自のコードをレビューしたいくつかの実験を示しています。彼らはいくつかを見つけただけでなく、多くの場合、潜在的なnull参照の識別は、設計に関するより広範な問題を示していました。

2番目の参照は、null参照エラーが問題であるというアサーションの具体的な証拠を提供しません。しかし、著者は、彼らの経験では、これらのnull参照エラーがソフトウェアの欠陥の重大な原因であると述べています。次に、このペーパーでは、これらの欠陥を根絶しようとする方法について説明します。

また、Eiffelの最近のリリースに関するISEからの発表で、これについて何かを見たことを思い出しました。彼らはこの問題を「ボイドセーフティ」と呼んでおり、バートランドメイヤー博士によって触発または開発された多くのものと同様に、問題の雄弁で教育的な説明と、言語とツールで問題を防ぐ方法について説明しています。詳細については、彼らの記事 http://doc.eiffel.com/book/method/void-safety-background-definition-and-toolsを読むことをお勧めします 。

Microsoftのコード契約についてもっと知りたい場合は、最近出てきた記事がたくさんあります。http:SLASH SLASH codecontracts.infoで私のブログをチェックすることもできます。このブログは、主に契約によるプログラミングの使用によるソフトウェア品質についての会話に専念しています。

于 2010-02-01T05:41:22.343 に答える
21

上記(Nullオブジェクト、空のコレクション)に加えて、いくつかの一般的な手法があります。つまり、リソースの取得はC ++の初期化(RAII)であり、Eiffelの契約による設計です。これらは要約すると次のようになります。

  1. 有効な値で変数を初期化します。
  2. 変数がnullになる可能性がある場合は、nullをチェックして特殊なケースとして扱うか、null参照例外を予期します(そしてそれに対処します)。アサーションは、開発ビルドでのコントラクト違反をテストするために使用できます。

私はこのようなコードをたくさん見ました:

if((value!= null)&&(value.getProperty()!= null)&& ... &&(... doSomethingUseful())

多くの場合、これは完全に不要であり、ほとんどのテストは、より厳密な初期化とより厳密なコントラクト定義を使用して削除できます。

これがコードベースの問題である場合は、それぞれの場合にnullが何を表すかを理解する必要があります。

  1. nullが空のコレクションを表す場合は、空のコレクションを使用します。
  2. nullが例外的なケースを表す場合は、例外をスローします。
  3. nullが誤って初期化されていない値を表す場合は、明示的に初期化してください。
  4. nullが正当な値を表す場合は、それをテストします。または、null操作を実行するNullObjectを使用することをお勧めします。

実際には、設計レベルでのこの明確さの基準は自明ではなく、コードベースに一貫して適用するには労力と自己規律が必要です。

于 2009-12-22T01:03:18.307 に答える
7

あなたはそうしない。

むしろ、C#でNREを「防止」するために特別なことは何もしません。ほとんどの場合、NREは単なる論理エラーの一種です。パラメータをチェックし、次のような多くのコードを使用することで、インターフェイスの境界でこれらをファイアウォールで保護できます。

void Foo(Something x) {
    if (x==null)
        throw new ArgumentNullException("x");
    ...
}

いたるところに(.Net Frameworkの多くがこれを実行します)、失敗したときに、もう少し有益な診断を取得します(ただし、スタックトレースはさらに価値があり、NREもそれを提供します)。しかし、それでも例外が発生します。

(余談ですが、次のような例外-NullReferenceException、ArgumentNullException、ArgumentException、...-通常はプログラムでキャッチするべきではなく、単に「このコードの開発者、バグがあります。修正してください」という意味です。これらを参照します。 「設計時」の例外として。これらを、ランタイム環境(FileNotFoundなど)の結果として発生し、プログラムによってキャッチおよび処理される可能性のある真の「ランタイム」例外と比較してください。)

しかし、結局のところ、正しくコーディングする必要があります。

'null'は多くの型/変数の無意味な値であるため、理想的にはNREの大部分は発生しません。理想的には、静的型システムはそれらの特定の型/変数の値として'null'を許可しません。そうすれば、コンパイラーは、このタイプの偶発的なエラーの発生を防ぎます(特定のクラスのエラーを除外することが、コンパイラーと型システムが最も得意とするものです)。これは、特定の言語と型システムが優れているところです。

ただし、これらの機能がない場合は、コードをテストして、このタイプのエラーのあるコードパスがないことを確認します(または、追加の分析を実行できる外部ツールを使用する可能性があります)。

于 2009-12-22T00:50:25.910 に答える
5

ここでは、 Nullオブジェクトパターンを使用することが重要です。

コレクションが入力されていない場合は、コレクションをnullではなく空にする必要があります。空のコレクションが必要なときにnullコレクションを使用することは混乱を招き、多くの場合不要です。

最後に、可能な限り、構築時にオブジェクトにnull以外の値をアサートさせます。そうすれば、後で値がnullであるかどうかについて疑問がなくなり、必要な場合にのみnullチェックを実行する必要があります。私のフィールドとパラメーターのほとんどについて、以前のアサーションに基づいて値がnullではないと想定できます。

于 2009-12-22T00:22:05.187 に答える
5

例外が発生する前にnull参照を簡単にチェックできますが、通常はそれが実際の問題ではないため、データがないとコードを実際に続行できないため、とにかく例外をスローすることになります。

多くの場合、主な問題は、null参照があるという事実ではなく、そもそもnull参照を取得しているという事実です。参照がnullであると想定されていない場合は、適切な参照がなくても、参照が初期化されるポイントを超えてはなりません。

于 2009-12-22T00:27:50.230 に答える
4

実際、あなたの言語にnull値がある場合、それは必ず発生します。null参照エラーは、アプリケーションロジックのエラーに起因します。したがって、それらすべてを回避できない限り、いくつかのエラーが発生する可能性があります。

于 2009-12-22T00:20:24.783 に答える
4

私が見た中で最も一般的なnull参照エラーの1つは、文字列からのものです。チェックがあります:

if(stringValue == "") {}

しかし、文字列は実際にはnullです。そのはず:

if(string.IsNullOrEmpty(stringValue){}

また、オブジェクトのメンバー/メソッドにアクセスする前に、過度に注意して、オブジェクトがnullでないことを確認することもできます。

于 2009-12-22T00:23:45.750 に答える
3

1つの方法は、可能な場合はNull値オブジェクト(別名Nullオブジェクトパターン)を使用することです。詳細はこちら

于 2009-12-22T00:18:19.063 に答える
2

構造化された例外処理を適切に使用すると、このようなエラーを回避できます。

また、単体テストは、コードが期待どおりに動作することを確認するのに役立ちます。たとえば、値が想定されていないときにnullにならないようにすることもできます。

于 2009-12-22T00:20:58.157 に答える
2

NullReferenceExceptionsを回避する最も簡単な方法の1つは、クラスコンストラクター/メソッド/プロパティセッターでnull参照を積極的にチェックし、問題に注意を向けることです。

例えば

public MyClass
{
   private ISomeDependency m_dependencyThatWillBeUsedMuchLater 

   // passing a null ref here will cause 
   // an exception with a meaningful stack trace    
   public MyClass(ISomeDependency dependency)
   {
      if(dependency == null) throw new ArgumentNullException("dependency");

      m_dependencyThatWillBeUsedMuchLater = dependency;
   }

   // Used later by some other code, resulting in a NullRef
   public ISomeDependency Dep { get; private set; }
}

上記のコードでnullrefを渡すと、呼び出し元のコードがタイプを誤って使用していることがすぐにわかります。null参照チェックがなかった場合、エラーはさまざまな方法で隠される可能性があります。

.NET Frameworkライブラリはほとんどの場合早期に失敗し、無効な場所でnull参照を提供すると失敗することがよくあります。スローされた例外は明示的に「めちゃくちゃだ!」と言っているので。理由を説明します。欠陥のあるコードの検出と修正は簡単な作業になります。

NullReferenceExceptionが必要なすべてであるため、この方法は過度に冗長で冗長であるという苦情を一部の開発者から聞いていますが、実際には大きな違いがあります。これは特に、呼び出しスタックが深い場合やパラメータが保存されていて、その使用が後になるまで延期される場合に当てはまります(おそらく、別のスレッドで、または他の方法で隠されています)。

どちらかと言えば、entryメソッドでのArgumentNullException、またはその内臓でのあいまいなエラーは何ですか?エラーの原因から離れるほど、エラーの追跡が難しくなります。

于 2009-12-22T01:05:12.077 に答える
2

ここでは、優れたコード分析ツールが役立ちます。優れた単体テストは、コード内の可能なパスとしてnullを考慮するツールを使用している場合にも役立ちます。「警告をエラーとして扱う」というスイッチをビルド設定に入れて、プロジェクトの警告の数を0に保つことができるかどうかを確認してください。警告が多くのことを示している場合があります。

覚えておくべきことの1つは、null参照例外をスローするのは良いことかもしれないということです。なんで?実行すべきコードが実行されなかったことを意味する場合があるためです。デフォルト値に初期化することは良い考えですが、問題を隠してしまうことがないように注意する必要があります。

List<Client> GetAllClients()
{
    List<Client> returnList = new List<Client>;
    /* insert code to go to data base and get some data reader named rdr */
   for (rdr.Read()
   {
      /* code to build Client objects and add to list */
   }

   return returnList;
}

了解しました。これで問題ないように見えるかもしれませんが、ビジネスルールによっては、これが問題になる場合があります。もちろん、null参照をスローすることはありませんが、Userテーブルが空になることはありませんか?アプリを適切な場所で回転させて、「空白の画面です」というユーザーからのサポートコールを生成しますか、それともどこかにログが記録される可能性のある例外を発生させてアラートをすばやく発生させますか?実行していることと「処理」例外を検証することを忘れないでください。これが、一部の言語からnullを取り除くことを嫌う理由の1つです...新しいバグが発生する可能性がある場合でも、バグを見つけやすくなります。

注意:例外を処理し、非表示にしないでください。

于 2009-12-22T01:45:39.543 に答える
1

プレーンコードソリューション

変数、プロパティ、およびパラメータを「null不可」としてマークすることにより、null参照エラーを早期にキャッチするのに役立つ構造体をいつでも作成できます。Nullable<T>方法が機能するように概念的にモデル化された例を次に示します。

[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T : class
{
    private T _value;

    public T Value
    {
        get
        {
            if (_value == null)
            {
                throw new Exception("null value not allowed");
            }

            return _value;
        }
        set
        {
            if (value == null)
            {
                throw new Exception("null value not allowed.");
            }

            _value = value;
        }
    }

    public static implicit operator T(NotNull<T> notNullValue)
    {
        return notNullValue.Value;
    }

    public static implicit operator NotNull<T>(T value)
    {
        return new NotNull<T> { Value = value };
    }
}

を使用するのとまったく同じ方法を使用しますがNullable<T>、正反対のことを達成することを目的とします-許可しないことを除きますnull。ここではいくつかの例を示します。

NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null

NotNull<T>は暗黙的にキャストされたり、キャストされたりするTため、必要な場所で使用できます。たとえば、 :Personを取るメソッドにオブジェクトを渡すことができます。NotNull<Person>

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull<Person> person)
{
    Console.WriteLine(person.Value.Name);
}

nullableの場合と同様に上記でわかるように、Valueプロパティを介して基になる値にアクセスします。または、明示的または暗黙的なキャストを使用することもできます。以下の戻り値の例をご覧ください。

Person person = GetPerson();

public static NotNull<Person> GetPerson()
{
    return new Person { Name = "John" };
}

または、キャストを実行してメソッドがT(この場合は)戻るだけの場合にも使用できます。Personたとえば、次のコードは上記のコードとまったく同じです。

Person person = (NotNull<Person>)GetPerson();

public static Person GetPerson()
{
    return new Person { Name = "John" };
}

拡張機能と組み合わせる

拡張方法と組み合わせるNotNull<T>と、さらに多くの状況をカバーできます。拡張メソッドがどのように見えるかの例を次に示します。

[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
    public static T NotNull<T>(this T @this) where T : class
    {
        if (@this == null)
        {
            throw new Exception("null value not allowed");
        }

        return @this;
    }
}

そして、これがどのように使用できるかの例です:

var person = GetPerson().NotNull();

GitHub

参考までに、上記のコードをGitHubで利用できるようにしました。次の場所で見つけることができます。

https://github.com/luisperezphd/NotNull

于 2016-03-08T01:49:20.500 に答える
0

nullを置き換えることができる正当なオブジェクトが存在する可能性がある場合は、NullObjectパターンSpecialCaseパターンを使用できます。

そのようなオブジェクトを構築できない場合、必須の操作を実装する方法がないため、Map-Reduceクエリなどの空のコレクションに依存できます。

もう1つの解決策は、オプション機能タイプです。これは、0個または1個の要素を持つコレクションです。そうすることで、実行できない操作をスキップする機会が得られます。

これらは、null参照やnullチェックなしでコードを書くのに役立つオプションです。

于 2015-06-10T09:13:29.980 に答える
0

役立つツール

役立つライブラリもいくつかあります。Microsoftコード契約は上記のとおりです。

他のいくつかのツールには、特に次の属性を使用する場合に、コードの記述中に警告を提供できるResharperが含まれています。NotNullAttribute

次のような属性を使用できるPostSharpもあります。

public void DoSometing([NotNull] obj)

これを行い、PostSharpをビルドプロセスの一部にすることobjで、実行時にnullがチェックされます。参照:PostSharpnullチェック

Fodyコードウィービングプロジェクトには、ヌルガードを実装するためのプラグインがあります。

于 2016-03-08T01:52:31.957 に答える
0

適切な「elsecase」なしでnullをうまく回避することは、プログラムが失敗しないことを意味しますが、どちらも修正されません。オプションは、Java API全体がオプションを返さない限り、どちらも役に立ちませんが、それまでに、どこでもnullをチェックするかのように、どこでも何もチェックしないように強制されます。結局、違いはありません。

将来、人々はチェックせずにfalseを返さないようにするために、別のオブジェクト「Falsable」を発明する可能性があります。笑

ロジックを理解し、必要に応じてチェックするだけで役立ちます。オプションではありません。それは単なる誤った安全です。

于 2021-02-10T14:28:28.140 に答える
-1

NullReferenceExceptionは、アセンブリにメソッドが見つからない場合に表示される可能性があります。たとえば、アセンブリがSportsMiniCar、miがMiniVanのインスタンスで、TellChildToBeQuietがアセンブリのメソッドである場合、例:m0 = mi.GetType()。GetMethod( "TellChildToBeQuiet") 。上記のメソッドを含むこのアセンブリバージョン2.0.0.0がGACに配置されていることを確認することで、これを回避できます。例:パラメータを使用したメソッドの呼び出し: `

enter code here

using System;
using System.Rwflection;
using System.IO;
using Carlibraries;
namespace LateBinding
{
public class program
{
   static void Main(syring[] args)
   {
         Assembly a=null;
         try
         {
              a=Assembly.Load("Carlibraries");
         }
         catch(FileNotFoundException e)
         {
               Console.Writeline(e.Message);
               Console.ReadLine();
               return;
         }
         Type miniVan=a.GetType("Carlibraries.MiniVan");
         MiniVan mi=new MiniVan();
         mi.TellChildToBeQuiet("sonu",4);
         Console.ReadLine();
       }
   }
   }

MiniSportsCarアセンブリをTellChildToBeQuiet(string ChildName、int count)で更新することを忘れないでください

于 2017-07-16T23:03:42.607 に答える