web-dev-qa-db-ja.com

C#の「OnError ResumeNext」の最良の代替手段は何ですか?

C#コードに空のcatchブロックを配置した場合、VB.NETの「OnErrorResumeNext」ステートメントと同等になりますか。

try
{
    C# code;
}

catch(exception)
{
}

私がこれを求めている理由は、VB.NETコードをC#に変換する必要があり、新しいコードで適切なtry {} catch {}を使用しているにもかかわらず、古いコードには最大200の「OnErrorResumeNext」ステートメントがあるためです。しかし、より良い代替手段はありますか?

14
Neel

VBプログラマーは、(悪い)習慣から多くのOn Error Resume Nextステートメントを含むコードを散らかしていることがよくあります。私の提案はno抑制で始めることです。例外を確認し、実際に何が壊れているかを確認します。思ったほど多くの問題はないかもしれません。逆に、実行できる回帰テストが多いほど良いです。エラーが無視された場合にのみ機能するエッジケースがいくつかある場合があります。

最終的には、エラー処理戦略を決定する必要があります。それは、多くのtry/catchブロック内で正常に巻き戻すか、エラーをトップレベルのハンドラーに浸透させるかです(どちらの戦略にも用途があります)。

期限に間に合うようにいくつかの例外を抑制しなければならない場合は、少なくともそれらの例外をログに記録して、コードで作業している次の開発者が空のtry/catchによって焼き付けられないようにします。 。

19
Tim Medora

これは許容できる場合もありますが、一般的にはコードの臭いを示しています。発生した例外を飲み込みたいと100%確信している場合は、そのままの方法で実行できますが、通常、例外がスローされた場合は、すべき実行何か

一般に、適切に設計されたコードで同じ結果を達成できます。現在特定のエラーが発生している場合は、それを質問に追加しますが、好奇心から質問している場合は、同等のものはありません。それは良いことです。

6

いいえ、同じではありません。

On Error Resume Nextを使用する場合、エラーが発生するとVBは次の行にスキップします。try/ catchでは、エラー(例外)が発生すると実行がcatchブロックにジャンプします。

5
Jonathan Wood

On Error Resume Nextステートメントを1つずつ分析し、それらの目的が何であるかを確認する必要があります。いくつかは単にずさんなコードかもしれませんが、Visual Basic6.0コードのOn Error Resume Nextには正当な理由があります。

Visual Basic6.0コードでOn Error Resume Nextを使用する理由のいくつかの例:

  • 特定のキーがVisualBasic6.0コレクションに存在するかどうかを確認します。これを行う唯一の方法は、キーで要素にアクセスし、キーが存在しない場合に発生するエラーを処理することです。 .NETに変換する場合、これをキーの存在のチェックに置き換えることができます。

  • 文字列を整数に解析します。 .NETでは、 TryParse を使用できます。

5
Joe

On Error Resume Nextは確かに、合法的に使用されている以上に悪用されており、VB.NETでも役立つ場所があります。

すべてのプリンタパラメータのデフォルトなど、多数のExcelプロパティに値を割り当てるプログラムについて考えてみます。Excelには無数のプリンタパラメータがあります。新しいバージョンのExcelには、以前のバージョンではサポートされていないプロパティが含まれている可能性があり、各バージョンでサポートされているプロパティを特定するのは簡単ではありません。プロパティが存在する場合、プログラムは値を割り当てる必要がありますが、古いバージョンのExcelが使用されている場合はプロパティを無視します。

VB.NETでこれを行う「正しい」方法は、Excelの各バージョンでサポートされているプリンタープロパティを判別し、使用中のバージョンを読み取り、そのバージョンで実装されているプロパティにのみ割り当てることです。それには多くの調査といくつかのコードが必要になりますが、すべてほとんどメリットがありません。 On Error Resume Nextはより実用的な解決策になります。

そして、残念ながら、私は今まさにこの問題に直面しています。私が試みようとしている回避策は、エラーを無視して、ある値を別の値に割り当てるだけのサブルーチンを作成することです。各代入ステートメントの代わりに、このサブルーティングを呼び出します。それほどひどいわけではありませんが、それほど素晴らしいものでもありません。

4
Stephen Flaum

「OnErrorResume Next」では、VBのエキスパートレベルのエラー処理である「インラインエラー処理」が可能です。概念は、エラーに基づいてアクションを実行するか、有益な場合はエラーを無視して、エラーを1行ずつ処理することですが、コードを記述された順序で実行し、コードジャンプを使用しません。

残念ながら、多くの初心者は「On Error Resume Next」を使用して、すべてのエラーを無視することにより、アプリケーションを使用している人から能力の欠如または怠惰を隠しました。 Try/catchはブロックレベルのエラー処理であり、.NET以前の世界では設計と実装によって中間的でした。

VB.NETの「OnErrorResume Next」の問題は、実行中のコードのすべての行にerrオブジェクトが読み込まれるため、try/catchよりも遅いことです。このフォーラムが、On Error Resume Nextを使用することは悪い習慣であり、コードのゴミであると主張する非常識な回答をチェックして宣伝したことに、私は少し心配しています。これはC#フォーラムです。 C#プログラマーが、あまり精通していない別の言語でショットを撮るのに本当に使用する必要がありますか?

https://msdn.Microsoft.com/en-us/library/aa242093(v = vs.60).aspx

本当のVBの経験がない中級のC#プログラマーは、別の「Microsoft Net」言語に対する奇妙な軽蔑のために、C#を唖然とさせ、機能を制限しようとすべきではないと言われています。次のコードを検討してください。 :

//-Pull xml from file and dynamically create a dataset.
 string strXML = File.ReadAllText(@"SomeFilePath.xml");
 StringReader sr = new StringReader(strXML);
 DataSet dsXML = new DataSet();
 dsXML.ReadXml(sr);

string str1 = dsXML.Tables["Table1"].Rows[0]["Field1"].ToString();
string str2 = dsXML.Tables["Table2"].Rows[0]["Field2"].ToStrin();
string str3 = dsXML.Tables["Table3"].Rows[0]["Field3"].ToStrin();
string str4 = dsXML.Tables["Table4"].Rows[0]["Field4"].ToString();
string str5 = dsXML.Tables["Table5"].Rows[0]["Field5"].ToString();

Xmlが通常Field3の値を持っているが、そうでない場合もある場合。テーブルにフィールドが含まれていないという厄介なエラーが発生します。それが必要なデータではないので、そうでなければ私はあまり気にすることができませんでした。この場合、ON Error Resume Nextを使用すると、エラーを無視でき、Containsメソッドを使用してテーブル、行、列の組み合わせの存在を確認する変数を設定するコードの各行をコーディングする必要がなくなります。これは小さな例です。大きなファイルから何千ものテーブル、列、行の組み合わせを取得する可能性があります。また、ここでは、文字列変数にこの方法でデータを入力する必要があると想定します。これは未処理のコードであり、問​​題が発生します。

VB.NETおよびONエラーが次の実装を再開することを検討してください。

On Error Resume Next

        'Pull Xml from file And dynamically create a dataset.
        Dim strXML As String = File.ReadAllText("SomeFilePath.xml")
        Dim srXmL As StringReader = New StringReader(strXML)
        Dim dsXML As DataSet = New DataSet()
        dsXML.ReadXml(srXmL)

        'Any error above will kill processing. I can ignore the first two errors and only need to worry about dataset loading the XML.
        If Err.Number <> 0 Then
            MsgBox(Err.Number & Space(1) & Err.Description)
            Exit Sub 'Or Function
        End If

        Dim str1 As String = dsXML.Tables("Table1").Rows(1)("Field1").ToString()
        Dim str2 As String = dsXML.Tables("Table2").Rows(2)("Field2").ToString()
        Dim str3 As String = dsXML.Tables("Table3").Rows(3)("Field3").ToString()
        Dim str4 As String = dsXML.Tables("Table4").Rows(4)("Field4").ToString()

上記のコードでは、考えられる1つのエラー状態を処理するだけで済みました。 3番目のエラーが処理される前に2つのエラーがあったとしても。 RAD開発の必要性On Error ResumeNext。C#は私の選択した言語ですが、RAD言語はVB多くの理由で、すべてのプログラマーが、いくつかの主要な言語(つまり、C)が実行され、未処理のエラーで実行を停止しないことを理解することを願っています。開発者の仕事は、必要と思われる場所でそれらをチェックすることです。 Microsoftの世界でそのパラダイムに最も近いもの。

幸い、.NETには、これらの状況を処理するための多くの高度な選択肢があります。私は含むに逃げました。したがって、C#では、言語の知識レベルを強化する必要があり、C#言語仕様に従って、このような問題を適切に回避します。煩わしいスローアウェイエラーを含む可能性のあるコードの繰り返し行の大きなブロックを処理するためのソリューションを検討してください。

try
            {
                if (!File.Exists(@"SomeFilePath.xml")) { throw new Exception("XML File Was Not Found!"); }
                string strXML = File.ReadAllText(@"SomeFilePath.xml");
                StringReader sr = new StringReader(strXML);
                DataSet dsXML = new DataSet();
                dsXML.ReadXml(sr);

                Func<string, string, int, string> GetFieldValue = (t, f, x) => (dsXML.Tables[t].Columns.Contains(f) && dsXML.Tables[t].Rows.Count >= x + 1) ? dsXML.Tables[t].Rows[x][f].ToString() : "";

                //-Load data from dynamically created dataset into strings.
                string str1 = GetFieldValue("Table1", "Field1", 0);
                string str2 = GetFieldValue("Table2", "Field2", 0);
                string str3 = GetFieldValue("Table3", "Field3", 0);
                //-And so on.

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            } 

Try/catchブロック内ではありますが、ラムダ関数は、xmlによって動的に入力されたデータセットからプルされているすべてのテーブル、行、列の組み合わせの存在をチェックしています。これは1行ずつチェックできますが、多くの余分なコードが必要になります(ここでは、同じ量の実行コードがありますが、維持するために記述されたコードははるかに少なくなっています)。残念ながら、これは「1行関数」のもう1つの悪い習慣と見なされる可能性があります。ラムダと無名関数の場合、私はそのルールを破ります。

.NETは、オブジェクトのステータスをチェックするための非常に多くの方法を提供しているため、エラー時の再開次は、.NET以前のようにVB専門家にとって重要ではありませんが、それでも周りにいるのはいいことです。特に、時間の無駄になる何かをコーディングしている場合は速くて汚いコーディングではありません。あなたへJava C#に変換します。Microsoftの世界に参加して、10000中間JavaとC#プログラマーが言うなら、あるトップレベルのMicrosoftGuru(VB言語と.NETを作成した人など)が.NET自体の開発において明らかに矛盾している場合、それは誤りであるため、それは真実でなければなりません。 C#とVBとF#と私が使用する必要のある他の言語で取得できるすべての機能が必要です。C#はエレガントですがVB =保有期間がはるかに長いため、より進化していますが、どちらも「同じこと」を行い、同じオブジェクトを使用しています。両方をよく学ぶか、比較会話でどちらかについてコメントしないでください。 90年代半ばusi高レベルのマイクロソフトテクノロジ。

3
Scott Net

「OnErrorResume Next」を使用することは、エラー処理には適していません(もちろん、これは私の個人的な意見ですが、ほとんどの開発者は私に同意しているようです)。他の人が以前の投稿でアドバイスしたように、Try...Catch...Finallyを使用してください( VB.NET またはC#のどちらでも)。

これはエラー処理のための非常に賢いオプションですが、エラー(空のcatchブロック)に対して何もすることもできません:)コードのすべての行(エラーを引き起こす可能性があります)を別々のTry...Catch Blockに配置することをお勧めします。エラーが発生した場合に、やりたいことが何でもできるようにします。ハッピーコーディングの人:)

2
vanu

「OnErrorResume Next」を発明した人たちは、それを作ったときに何かを考えていたと思います。あなたの質問への答えはノーです、C#のこの構成に相当するものは何もありません。 C#と.Netには、ケアと注意に非常に飢えている多くの機能があり、しばらくすると、すべての人の「例外的な動作」に対応するのに疲れます。ほとんどすべてが例外をスローする可能性がある場合、Word自体はそれをいくらか意味を失います。あなたはイテレーションの中にいます、そして数千万のアイテムがたまたま例外的であるならば、あなたは何をすべきですか?履歴書次は便利な答えの1つである可能性があります。

1
Alfred Molnar

「onerrorresume next」の適切な.NET置換は、Try ___メソッドの使用です。 Visual Basic 6.0では、キーがコレクションに存在するかどうかを確認するには、コレクションを手動で検索するか(非常に遅い)、インデックスを作成して、存在しない場合に発生したエラーをトラップする必要がありました。 VB.NETでは、Dictionaryオブジェクト(古いコレクションの改良版)はTryGetValueメソッドをサポートします。このメソッドは、値の取得が成功したかどうかを、エラーが発生してもエラーを発生させずに示します。そうではありません。他の多くの.NETオブジェクトも同様の機能をサポートしています。 「try」に相当する必要があるがそうではないメソッドがいくつかありますが(たとえば、Control.BeginInvoke)、それらを個別にTry/Catchでラップするのはそれほど面倒ではありません。

1
supercat

@Tim Medoraが言っているように、コーディング時にそのようなアプローチを使用しないように努力する必要があります。ただし、場合によっては便利であり、そのような動作をエミュレートすることは可能です。関数とその使用例を次に示します。 (一部のコード要素はC#6を使用して記述されていることに注意してください)

    /// <summary>
    /// Execute each of the specified action, and if the action is failed, go and executes the next action.
    /// </summary>
    /// <param name="actions">The actions.</param>
    public static void OnErrorResumeNext(params Action[] actions)
    {
        OnErrorResumeNext(actions: actions, returnExceptions: false);
    }

    /// <summary>
    /// Execute each of the specified action, and if the action is failed go and executes the next action.
    /// </summary>
    /// <param name="returnExceptions">if set to <c>true</c> return list of exceptions that were thrown by the actions that were executed.</param>
    /// <param name="putNullWhenNoExceptionIsThrown">if set to <c>true</c> and <paramref name="returnExceptions"/> is also <c>true</c>, put <c>null</c> value in the returned list of exceptions for each action that did not threw an exception.</param>
    /// <param name="actions">The actions.</param>
    /// <returns>List of exceptions that were thrown when executing the actions.</returns>
    /// <remarks>
    /// If you set <paramref name="returnExceptions"/> to <c>true</c>, it is possible to get exception thrown when trying to add exception to the list. 
    /// Note that this exception is not handled!
    /// </remarks>
    public static Exception[] OnErrorResumeNext(bool returnExceptions = false, bool putNullWhenNoExceptionIsThrown = false, params Action[] actions)
    {
        var exceptions = returnExceptions ? new Collections.GenericArrayList<Exception>() : null;
        foreach (var action in actions)
        {
            Exception exp = null;
            try { action.Invoke(); }
            catch (Exception ex) { if(returnExceptions) { exp = ex; } }

            if (exp != null || putNullWhenNoExceptionIsThrown) { exceptions.Add(exp); }
        }
        return exceptions?.ToArray();
    } 

例:代わりに:

        var a = 19;
        var b = 0;
        var d = 0;
        try { a = a / b; } catch { }
        try { d = a + 5 / b; } catch { }
        try { d = (a + 5) / (b + 1); } catch { }

あなたはできる:

            var a = 19;
            var b = 0;
            var d = 0;
            OnErrorResumeNext(
                () =>{a = a / b;},
                () =>{d = a + 5 / b;},
                () =>{d = (a + 5) / (b + 1);}
            );
1
Ilan

私はVB6の古い帽子です。最初の短いレッスン...

On Error ResumeNextを使用する理由があります。主に読みやすさのために。 VB6では、エラートラップを実装する方法が2つあります。このように、エラー再開時に「インライン」を使用できます。

On Error Resume Next
<something that may throw an error>
If Err.Number <> 0 Then
   <do something about this specific line of code>
   Err.Clear()
End If

または、これが表示される場合があります。

Sub DoSomething

   On Error Goto Handler1
   <do something that causes an error>

   On Error Goto Handler2
   <do something that may cause an error>

   Exit Sub

   Handler1:
   <log error or something>
   Resume Next

   Handler2:
   <log error or something>
   Resume Next

End Sub  

しかし、古いVB6コードでは、これも表示される可能性があります...

Sub PerformThis
On Error Resume Next

End Sub

これらのケースをTryCatchに変換するのは非常に簡単です...エラーをシンクする必要がある場合は、[エラー再開]をすばやく「インライン」で確認してください。次にこれを実行します。

try { _objectinfo.Add(_object.attribute1); } catch (Exception _e) { }

コードをサブルーチンにカプセル化することで、try catchを呼び出し元のルーチンに上げることもできます...したがって、サブルーチン全体をシンクする必要がある場合は、これを実行してください...

try { PerformAction(); } catch (Exception _e) { }

PerformAction()サブルーチンのコードの先頭にOn Error Resume Nextが含まれている場合は、これを実行します。呼び出し元のサブルーチンでTryCatchを使用します。

幸運を...

0
Keith Aymar