11

注/免責事項:数回検索した後、この投稿に最も近いのは、SO(メソッドチェーンと仕上げの問題)に関する投稿です。これは私の質問に似ていますが、実際には答えられませんが、とにかく、これが重複した質問ではないことを願っています。

私がしていること:

一連のメソッド呼び出しのために、既存のロギングフレームワークのファサードとして流暢なインターフェイスを作成しました。そのため、構文は次のようになります。

Logger.Debug().Message("Debug message!").WriteToLog();
Logger.Error().Message("An exception occured").Exception(ex).WriteToLog();

あるメソッド呼び出しから次のオブジェクトに内部オブジェクトを渡して、最後の呼び出しが行われたときに(WriteToLogメソッド)。メッセージはどこかのログファイルに書き込まれます。

においがするビット

検証するために(アプリケーションがデバッグモードでビルドされている場合のみ)、チェーンが終了するまでメソッド呼び出しから返されたオブジェクトに渡されるコンテキストクラス(プロパティバッグオブジェクトのみ)にプロパティがあります。これはブール値であり、デフォルトはfalseです。

このプロパティは、Debug.Assertを使用してコンテキストクラスデストラクタで評価され、チェーンを終了する最後のメソッドが呼び出されたかどうかを判断して、開発中にログエラーを検出できるようにします。(プロパティ、プロパティを設定するコード、およびデストラクタ自体はすべて#if DEBUGプリプロセッサディレクティブのコンテキストで作成されるため、リリースに組み込まれている場合、またはシンボルが存在しない場合、コードは作成されません。コンパイルされます。)

c#2.0以降では、デストラクタの使用が不適切であり、ファイナライズ順序についての保証がないと思われるため、プロパティにアクセスできない可能性があることを知っています。これが、デバッグモードでビルドされた場合にのみ発生する理由であり、私がそれを避けたい理由です。

私がアサーションを作成しようとしている理由は、忘れがちで、次のようなコードを記述してしまうためです。

Logger.Debug().Message("Debug message!");

これは、大まかに見ると、ログに記録されるはずのように見えますが、何もログに記録されないことを意味します。

私の質問

私が知りたいのは、finalメソッドが常に呼び出されていることを確認する別の方法を誰かが考えられるでしょうか。これらのメッセージは、メソッドチェーンが終了していないことを開発者に強調するために、開発中に必要なだけです。エンドユーザーが最終製品へのログインに関連するエラーメッセージを見つけてほしくないのです。

4

1 に答える 1

11

まず第一に、この場合、流暢なインターフェースの必要性に疑問を投げかけます.はるかに単純なインターフェースで簡単にやり遂げることができるようです:

Logger.Debug.Message("Test");

または単に:

Logger.Debug("Test");

ただし、流暢なインターフェイスが本当に必要な場合は、これを行う別の方法として、流暢なインターフェイスが戻り値ではなくメソッドのパラメーターで機能するようにすることもできます。

したがって、これを行う代わりに:

Method1().Method2().Method3();

そして、最後の呼び出しを忘れます:

Method1().Method2().Method3().Execute();

代わりに、おそらく次のようにコードを整理します。

Method1(o => o.Method2().Method3());

これを行うには、すべての流暢なメソッドを呼び出すオブジェクトを定義します。

public class LoggerOptions
{
    public LoggerOptions Debug() { LoggerType = LoggerType.Debug; return this; }
    public LoggerOptions Error() { LoggerType = LoggerType.Error; return this; }
    public LoggerOptions Message(string message) { ...; return this; }

    public LoggerType Type { get; set; }
    ...
}

ここでのすべてのメソッド呼び出しは、LoggerOptions オブジェクトを変更し、同じインスタンスを返し、流暢なインターフェイスを継続します。

その後:

public static class Logger
{
    public static void Log(Func<LoggerOptions, LoggerOptions> options)
    {
        LoggerOptions opts = options(new LoggerOptions());
        // do the logging, using properties/values from opts to guide you
    }
}

次に、次のように呼び出します。

Logger.Log(opts => opts.Debug().Message("Debug message"));

options オブジェクトの設定を完了する前に絶対に呼び出す必要があるいくつかの端末メソッドがある場合は、別のオブジェクトを作成できます。

public class LoggerOptions
{
    public LoggerOptions Debug() { LoggerType = LoggerType.Debug; return this; }
    public LoggerOptions Error() { LoggerType = LoggerType.Error; return this; }
    public LoggerOptions Message(string message) { ...; return this; }

    public LoggerType Type { get; set; }
    ...

    public LoggerFinalOptions ToEventLog() { ...; return new LoggerFinalOptions(this); }
    public LoggerFinalOptions ToFile(string fileName) { ...; return new LoggerFinalOptions(this); }
}

その後:

public static class Logger
{
    public static void Log(Func<LoggerOptions, LoggerFinalOptions> options)
    {
        LoggerFinalOptions opts = options(new LoggerOptions());
        // do the logging, using properties/values from opts to guide you
    }
}

これにより、明示的な最終オプション オブジェクトを返す何かの呼び出しでメソッド チェーンを終了しない限り、コードをコンパイルできないことが保証されます。

// will not compile
Logger.Log(opts => opts.Debug().Message("Test"));

// will compile
Logger.Log(opts => opts.Debug().Message("Test").ToFile("log.log"));
于 2013-03-12T22:09:48.367 に答える