0

したがって、データ(文字列)を出力できるさまざまな潜在的なオブジェクトがいくつかあります。私ができるようにしたいのは、出力先を定義する潜在的な引数を使用して、汎用の Output.WriteLine 関数を実行することです。私がコードのために持っているもの -

//Defined in static class Const
public enum Out : int { Debug = 0x01, Main = 0x02, Code = 0x04 };

static class Output
{
    private static List<object> RetrieveOutputMechanisms(Const.Out output)
    {
        List<object> result = new List<object>();

    #if DEBUG
        if (bitmask(output, Const.Out.Debug))
            result.Add(1);//Console); //I want to add Console here, but its static
    #endif
        if (bitmask(output, Const.Out.Main))
            if (Program.mainForm != null)
                result.Add(Program.mainForm.Box);

        if (bitmask(output, Const.Out.Code))
            if (Program.code!= null)
                result.Add(Program.code.Box);

        return result;
    }

    public static void WriteLine(Color color, string str, Const.Out output = Const.Out.Debug & Const.Out.Main)
    {
        Console.WriteLine(
        List<object> writers = RetrieveOutputMechanisms(output);
        foreach (object writer in writers)
            writer.WriteLine(str, color);
    }
}

これのポイントは、これらの呼び出しが呼び出されたときに存在する場合と存在しない場合があるフォーム上にあるため、出力先が常に存在するとは限らないことです。したがって、アイデアは、印刷しようとしているものを特定し、それが存在するかどうかを判断し、印刷するもののリストに追加してから、「WriteLine」メソッドを実装している場合はループしてすべてに印刷することです.

私が遭遇した2つの問題は、

  1. そのコンソールは静的クラスであり、(私の知る限り) オブジェクト リストに適切に追加することはできません。
  2. リスト内のオブジェクトが WriteLine を定義していると主張し、それらを複数の基本型に適用されるものにキャストする方法がわかりません。このスキームでコンソールを適切に動作させることができると仮定すると、それは明らかな問題であり、実際のボックスと同じ基本タイプではありませんが、ボックスではないものがあれば、それは素晴らしいことです何かのようなもの

    foreach (ライターのオブジェクトライター) .WriteLine(str, color)

個別にキャストする必要がないように。

RetrieveOutputMechanisms 関数から単純に WriteLine を実行しない大きな理由は、WriteLine だけでなく、それ以上のものをカバーする必要があるためです。つまり、各関数にビットマスク コードをコピーする必要があります。

編集: パブリック プロパティを Program に追加するのは悪い考えだと思います.ぜひ詳しく教えてください。

4

3 に答える 3

1

1 つの方法は、Action(デリゲート) を使用して、それらを に保存することListです。これはConsole、ラムダ (または 2.0 デリゲート) を簡単に記述して、出力変数を呼び出されたメソッドの適切なパラメーターにマップできるため、他のクラスでも機能します。キャストの必要はありません。次のように機能します。

(これは、C# 3.5 以降を使用していることを前提としていますが、2.0 からデリゲートを使用すると、これらすべてを実行できます)

static class Output
{
    private static List<Action<string, Color>> RetrieveOutputMechanisms(Const.Out output)
    {
        List<Action<string, Color>> result = new List<string, Color>();

    #if DEBUG
        if (bitmask(output, Const.Out.Debug))
            result.Add((s, c) => Console.WriteLine(s, c)); //I want to add Console here, but its static
    #endif
        if (bitmask(output, Const.Out.Main))
            if (Program.mainForm != null)
                result.Add((s, c) => Program.mainForm.Box.WriteLine(s, c));

        if (bitmask(output, Const.Out.Code))
            if (Program.code!= null)
                result.Add((s, c) => Program.code.Box.WriteLine(s, c));

        return result;
    }

    public static void WriteLine(Color color, string str, Const.Out output = Const.Out.Debug & Const.Out.Main)
    {
        var writers = RetrieveOutputMechanisms(output);
        foreach (var writer in writers)
            writer(str, color);
    }
}

(編集して追加)

Outputこれをさらに大幅に変更して、クラスを「登録」して、クラス自体の特定の「出力メカニズム」の書き込みを実行できるようにすることができます。シングルトンを作成することもできますOutput(それを行うことに反対する意見もありますが、この目的のためにメイン プログラムに public static 変数を貼り付けるよりはましです)。元のクラスにさらに重要な変更を加えた例を次に示します。

public sealed class Output
{
    private Dictionary<Out, Action<string, Color>> registeredWriters = new Dictionary<Out, Action<string, Color>>();

    public static readonly Output Instance = new Output();

    private void Output() { } // Empty private constructor so another instance cannot be created.

    public void Unregister(Out outType)
    {
        if (registeredWriters.ContainsKey(outType))
            registeredWriters.Remove(outType);
    }

    // Assumes caller will not combine the flags for outType here
    public void Register(Out outType, Action<string, Color> writer)
    {
        if (writer == null)
            throw new ArgumentNullException("writer");

        if (registeredWriters.ContainsKey(outType))
        {
            // You could throw an exception, such as InvalidOperationException if you don't want to 
            // allow a different writer assigned once one has already been.
            registeredWriters[outType] = writer;
        }
        else
        {
            registeredWriters.Add(outType, writer);
        }
    }

    public void WriteLine(Color color, string str, Const.Out output = Const.Out.Debug & Const.Out.Main)
    {
        bool includeDebug = false;
        #if DEBUG
        includeDebug = true;
        #endif

        foreach (var outType in registeredWriters.Keys)
        {
            if (outType == Const.Out.Debug && !includeDebug)
                continue;

            if (bitmask(output, outType))
                registeredWriters[outType](str, color);
        }
    }
}

次に、プログラムの他の場所 (フォーム クラスなど) でライターを登録するには、次のようにします。

Output.Instance.Register(Const.Out.Main, (s, c) => this.Box.WriteLine(s, c));

フォームがアンロードされると、次のことができます。

Output.Instance.Unregister(Const.Out.Main);

次に、シングルトンを使用しない別の方法があります。次に、さまざまな目的で複数のOutputインスタンスを作成し、これらを他のクラスに注入できます。たとえば、メイン フォームのコンストラクターを変更してOutputパラメーターを受け入れ、これを後で使用するためにオブジェクト変数として保存します。メイン フォームは、これを必要とする子フォームに渡すことができます。

于 2013-07-18T18:17:16.443 に答える
0

書き込む必要のあるデータを持つオブジェクトが次のように動作する場合:

A は常にコンソールに書き込み、ログ B は常にログに書き込みます C は常にコンソールに書き込みます

すべてのデータについて、最善の策は、インターフェイスを宣言し、それぞれに出力用のインターフェイス メソッドを実装させることです。次に、呼び出し元のコードで、それらを実際の型として宣言するのではなく、IOutput 型またはメソッドを持つインターフェイス u を呼び出す代わりに宣言します。次に、2 つのヘルパー メソッドを用意します。1 つは実際にコンソールに出力するためのもので、もう 1 つは実際にログ ファイルに出力するためのものです。A は両方のヘルパー、B と C をそれぞれのヘルパーと呼びます。

一方、オブジェクトが異なる時間にさまざまなログに書き込む場合:

A、B、および C は、いくつかのプロパティに応じて、コンソールに書き込むこともあれば、ログに書き込むこともあります。

次に、クラスが何かを書きたい場合に備えて、イベント ハンドラーを作成することをお勧めします。次に、コンソールへの書き込みとログへの書き込みを識別するロジックをリスナークラスに用意し、適切なものをその出力イベントに添付します。そうすれば、その機能だけをカプセル化するクラスのどこに何が書き込まれるかについてのロジックを保持しながら、A、B、および C クラスを依存関係から解放したままにすることができます。ビットマスクを使用するモノリシックな方法を検討してください。A、B、または C のロギングの動作が変更されるとすぐに、または新しい出力を追加する必要がある場合、突然、1 つのクラスまたはメソッドがそれらすべてに同時に影響することを心配する必要があります。これにより、保守性が低下し、バグのテストが難しくなります。

于 2013-07-18T14:34:17.237 に答える