9

次のように使用される C# の「ステータス」クラスがあります。

Status MyFunction()
{
   if(...) // something bad
     return new Status(false, "Something went wrong")
   else
     return new Status(true, "OK");
}

あなたはアイデアを得る。MyFunction のすべての呼び出し元は、返された Status を確認する必要があります。

Status myStatus = MyFunction();
if ( ! myStatus.IsOK() )
   // handle it, show a message,...

ただし、怠惰な呼び出し元はステータスを無視できます。

MyFunction(); // call function and ignore returned Status

また

{
  Status myStatus = MyFunction(); 
} // lose all references to myStatus, without calling IsOK() on it

これを不可能にすることは可能ですか?例外のスローなど

一般に、特定の関数を呼び出さなければならない C# クラスを作成することは可能ですか?

C++ バージョンの Status クラスでは、デストラクタでプライベート bool bIsChecked に対するテストを記述し、誰かがこのインスタンスをチェックしない場合にベルを鳴らすことができます。

C# の同等のオプションは何ですか? 「C#クラスにデストラクタは必要ない」とどこかで読みました

IDisposable インターフェイスの Dispose メソッドはオプションですか?

この場合、解放するアンマネージ リソースはありません。さらに、GC がいつオブジェクトを破棄するかは決定されません。最終的に破棄されたときに、その特定の Status インスタンスをいつどこで無視したかを知ることは可能ですか? 「using」キーワードは役に立ちますが、怠惰な呼び出し元には必要ありません。

4

11 に答える 11

10

これがあなたの質問に直接答えないことはわかっていますが、関数内で「何かがうまくいかなかった」場合(予期しない状況)、ステータスリターンコードを使用するのではなく、例外をスローする必要があると思います.

次に、この例外をキャッチして処理できる場合は呼び出し元に任せ、呼び出し元が状況を処理できない場合は伝播を許可します。

スローされる例外は、適切な場合、カスタム タイプである可能性があります。

予想される別の結果については、@Jon Limjap の提案に同意します。私は bool の戻り値の型が好きで、メソッド名の前に「Try」を付けます。

bool TryMyFunction(out Status status)
{
}
于 2008-08-21T10:02:28.637 に答える
7

ユーザーが MyFunction の結果を取得することを本当に必要とする場合は、代わりにそれを無効にして、out または ref 変数を使用することをお勧めします。

void MyFunction(out Status status)
{
}

見苦しく見えるかもしれませんが、少なくとも、変数が必要な結果を取得する関数に渡されることを保証します。

@イアン、

例外の問題は、それが頻繁に発生するものである場合、例外のためにシステム リソースを使いすぎている可能性があることです。例外は、完全に予期されるメッセージではなく、例外的なエラーに対して実際に使用する必要があります。

于 2008-08-21T10:28:25.920 に答える
3

返された HTTP ステータス コードがエラー コードの場合、System.Net.WebRequest でも例外がスローされます。これを処理する一般的な方法は、try/catch をラップすることです。catch ブロックのステータス コードは引き続き無視できます。

ただし、Action< Status> のパラメーターを使用して、呼び出し元がステータスを受け入れるコールバック関数を強制的に渡してから、それを呼び出したかどうかを確認するようにすることもできます。

void MyFunction(Action<Status> callback)
 { bool errorHappened = false;

   if (somethingBadHappend) errorHappened = true;

   Status status = (errorHappend)
                     ? new Status(false, "Something went wrong")
                     : new Status(true, "OK");
   callback(status)

   if (!status.isOkWasCalled) 
     throw new Exception("Please call IsOK() on Status"). 
 }

MyFunction(status => if (!status.IsOK()) onerror());

何もせずに IsOK() を呼び出すことが心配な場合は、代わりに Expression< Func< Status,bool>> を使用してください。ラムダを分析して、ステータスに対する処理を確認できます。

void MyFunction(Expression<Func<Status,bool>> callback)
 { if (!visitCallbackExpressionTreeAndCheckForIsOKHandlingPattern(callback))
     throw new Exception
                ("Please handle any error statuses in your callback");


   bool errorHappened = false;

   if (somethingBadHappend) errorHappened = true;

   Status status = (errorHappend)
                     ? new Status(false, "Something went wrong")
                     : new Status(true, "OK");

   callback.Compile()(status);
 }

MyFunction(status => status.IsOK() ? true : onerror());

または、ステータス クラスを完全に放棄して、成功の場合は 1 つのデリゲートを渡し、エラーの場合は別のデリゲートを渡します。

void MyFunction(Action success, Action error)
 { if (somethingBadHappened) error(); else success();
 }

MyFunction(()=>;,()=>handleError()); 
于 2008-08-21T10:23:18.540 に答える
2

メソッドからの戻り値として必要な効果を得ることはできないと確信しています。C# は、C++ でできることのいくつかを実行できません。ただし、同様の効果を得るためのやや醜い方法は次のとおりです。

using System;

public class Example
{
    public class Toy
    {
        private bool inCupboard = false;
        public void Play() { Console.WriteLine("Playing."); }
        public void PutAway() { inCupboard = true; }
        public bool IsInCupboard { get { return inCupboard; } }
    }

    public delegate void ToyUseCallback(Toy toy);

    public class Parent
    {
        public static void RequestToy(ToyUseCallback callback)
        {
            Toy toy = new Toy();
            callback(toy);
            if (!toy.IsInCupboard)
            {
                throw new Exception("You didn't put your toy in the cupboard!");
            }
        }
    }

    public class Child
    {
        public static void Play()
        {
            Parent.RequestToy(delegate(Toy toy)
            {
                toy.Play();
                // Oops! Forgot to put the toy away!
            });
        }
    }

    public static void Main()
    {
        Child.Play();
        Console.ReadLine();
    }
}

非常に単純な例では、Parent.RequestToy を呼び出してデリゲートを渡すことで、Toy のインスタンスを取得します。おもちゃを返す代わりに、メソッドはすぐにおもちゃでデリゲートを呼び出します。これは、戻る前に PutAway を呼び出す必要があります。そうしないと、RequestToy メソッドが例外をスローします。この手法を使用することの賢明さについては主張しません-実際、すべての「何かがうまくいかなかった」例では、例外はほぼ間違いなくより良い賭けです-しかし、元の要求にできるだけ近づくと思います.

于 2008-08-21T13:17:05.203 に答える
1

誰かにステータスのチェックを強制するのではなく、プログラマーがそうしないことのリスクを認識しており、そのような行動をとる理由があると想定する必要があると思います。機能が将来どのように使用されるか分からないので、そのような制限を設けることは可能性を制限するだけです。

于 2008-08-21T11:51:53.990 に答える
1

戻り値として Status を使用すると、C プログラミングの「昔」を思い出します。何かがうまくいかなかった場合に 0 未満の整数を返したときのことです。

(あなたが言ったように)何かがうまくいかなかったときに例外をスローした方がいいと思いませんか?「怠惰なコード」が例外をキャッチしない場合は、確実にわかります。

于 2008-08-21T10:11:35.680 に答える
0

コードが要求を発行するオブジェクトが単一のスレッド (*) によってのみ使用される場合に役立つ場合があるパターンの 1 つは、オブジェクトをエラー状態に保ち、操作が失敗した場合、オブジェクトは次の処理が行われるまで使用できなくなることです。エラー状態がリセットされます (将来のリクエストは、できれば以前の失敗と新しいリクエストの両方に関する情報を含む即時の例外をスローすることによって、すぐに失敗する必要があります)。呼び出しコードがたまたま問題を予測する場合、これにより、呼び出しコードは、例外がスローされた場合よりも問題をより明確に処理できる場合があります。呼び出し元のコードによって無視されない問題は、通常、発生後すぐに例外をトリガーすることになります。

(*) リソースが複数のスレッドによってアクセスされる場合は、スレッドごとにラッパー オブジェクトを作成し、各スレッドの要求が独自のラッパーを通過するようにします。

このパターンは、例外が使用できないコンテキストでも使用でき、そのような場合に非常に実用的な場合があります。ただし、通常は、try/do パターンのいくつかのバリエーションの方が優れています。呼び出し元が (TryXXメソッドを使用して) 失敗が予想されることを明示的に示さない限り、失敗時にメソッドが例外をスローするようにします。発信者が失敗が予想されると言いながら、それを処理しない場合、それは彼らの問題です。上記のスキームを使用して、try/do を 2 番目の保護層と組み合わせることができますが、コストに見合うかどうかはわかりません。

于 2014-10-02T21:38:10.043 に答える
0

@Paul Extensible C#を使用してコンパイル時に実行できます。

于 2008-08-21T10:42:52.173 に答える
0

次の方法で例外をスローできます。

throw MyException;


[global::System.Serializable]
        public class MyException : Exception
        {
        //
        // For guidelines regarding the creation of new exception types, see
        //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
        // and
        //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
        //

        public MyException () { }
        public MyException ( string message ) : base( message ) { }
        public MyException ( string message, Exception inner ) : base( message, inner ) { }
        protected MyException (
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context )
            : base( info, context ) { }
    }

上記の例外は、要件に合わせて完全にカスタマイズできます。

私が言いたいことの 1 つはこれです。戻りコードを確認するのは呼び出し元に任せます。手段とインターフェイスを提供するのは呼び出し元の責任です。また、例外をスローするよりも、リターン コードを使用して if ステートメントでステータスを確認する方がはるかに効率的です。それが本当に例外的な状況である場合は、必ず破棄してください...しかし、デバイスを開くことができなかった場合は、リターン コードを使用する方が賢明かもしれません。

于 2008-08-21T10:02:44.530 に答える
0

式ではなく、コンパイラにチェックさせるとよいでしょう。:/しかし、それを行う方法はありません...

于 2008-08-21T10:07:18.187 に答える
0

GCCには、warn_unused_resultこの種のものに最適な属性があります。おそらく、Microsoft のコンパイラにも似たようなものがあります。

于 2008-08-21T10:31:08.940 に答える