0

この質問に関心をお寄せいただきありがとうございます。関係するコードをより明確にするように求められた方もいらっしゃるので、もう少し情報を提供するために、それを編集してもう少し詳細を提供します。

前の質問に関連して、WPFウィンドウで基本的なコンソールをエミュレートしようとしています(テキスト出力のみ)。これは、バックグラウンドで実行されている多くのコードが別々のスレッドで実行されているプログラムで動作することを目的としています。このコードはwhileループにも大きく依存しているため、私の計画では、WPFコンソールウィンドウをメインスレッドに保持し(必要になる可能性のある追加のGUIウィンドウとともに)、すべてのコードを別々のスレッドで実行する予定です。

ウィンドウには、次のように使用されるWriteLineメソッドがあります。

mainConsole.WriteLine("This is a message for the user.", SomeSender);

残りのコードは、このメソッドを定期的に呼び出す必要があります。

追加情報:

ウィンドウ自体は、スクローラーでラップされたテキストブロックで構成されています。ウィンドウのWriteLineメソッドは、メッセージとフォーマット(フォント、フォントサイズ、および色-メッセージの送信者によって異なります)をこの情報を含むオブジェクトのリストに追加し、これらのメッセージのリストを表示します。書式設定)をTextblockのコンテンツとして。このメソッドは意図したとおりに機能するため、書き直す必要はなく、アクセス可能である必要があります。

私はこの説明をできるだけ簡潔に保つように努めました。詳細については、前の質問を参照してください。

だから私の質問は今です:ウィンドウのWriteLineメソッドを任意のクラスのすべてのスレッドで使用できるようにして、Console.WriteLine()のように使用できるようにする効率的な方法はありますか?

4

4 に答える 4

1

いくつかのオプションがありますが、あなたの場合、誰でも、どこでも、コンソールに書き込むことができるのは本当に理にかなっているように思えます。それを考えると、私は次のようなものを作成します:

public class MyConsole
{
    public static event Action<string> TextWritten;
    public static void Write(object obj)
    {
        string text = (obj ?? "").ToString();
        if (TextWritten != null)
            TextWritten(text);
    }

    public static void WriteLine(object obj)
    {
        Write(obj + "\n");
    }
}

次に、コンソール フォームにTextWrittenイベントをサブスクライブさせ、テキストが書き込まれたら、そのテキストをコンソールに書き込みます。(必ず最初に UI スレッドにマーシャリングしてください。)

ここで and イベントを使用することの主な利点は、このクラスでフォームを直接処理するのとは対照的に、追加のイベント ハンドラーを簡単に追加して、標準の入出力とやり取りしたり、ファイルに追加のログを追加したり、複数のログを保持したりできることです。コンソール フォームを一度に開くなど。この柔軟性は、デバッグ (つまり、フラット ファイルへの追加の書き込み) と本番環境 (標準の in/out によるリダイレクトがはるかに容易になる) の両方に役立ちます。

于 2013-01-21T17:58:30.853 に答える
1

コード内のどこからでもログにアクセスできるログ サービスを作成しようとしているようです。スレッドについて言及しているため、注意してその同期を適切に処理する必要があります。

ILogger最初に次のようなインターフェイスを作成します。

public interface ILogger
{
  void Log(string line);
  void Log(string format, params object[] args);
}

次に、適切なLogger基本クラス:

public abstract class Logger : ILogger
{
  public abstract void Log(string line);

  public virtual void Log(string format, params object[] args)
  {
    Log(string.Format(format, args));
  }
}

もちろん、実際の実装が必要になります。

using System.Collections.Concurrent;
using System.Threading.Tasks;

public class ConcurrentLogger : Logger, ILogger, IDisposable
{
  bool isDisposed;
  BlockingCollection<string> loggedLines;
  Action<string> callback;

  public ConcurrentLogger(Action<string> callback)
  {
    if (callback == null)
      throw new ArgumentNullException("callback");

    var queue = new ConcurrentQueue<string>();
    this.loggedLines = new BlockingCollection<string>(queue);

    this.callback = callback;

    StartMonitoring();
  }

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool isDisposing)
  {
    if (isDisposed) return;

    if (isDisposing)
    {
      if (loggedLines != null)
        loggedLines.CompleteAdding();
    }

    isDisposed = true;
  }

  public override void Log(string line)
  {
    if (!loggedLines.IsAddingCompleted)
      loggedLines.Add(line);
  }

  protected virtual void StartMonitoring()
  {
    Task.Factory.StartNew(() =>
      {
        foreach (var line in loggedLines.GetConsumingEnumerable())
        {
          if (callback != null)
            callback(line);
        }

        loggedLines.Dispose();

      }, TaskCreationOptions.LongRunning);
  }
}

グローバル アクセスにはクラスが必要Singletonになるため、このLogManagerクラスを作成します。

public sealed class LogManager : ILogger
{
  #region Singleton
  static readonly LogManager instance = new LogManager();

  public static LogManager Current { get { return instance; } }

  private LogManager() { } // Disallow creating instances.
  #endregion

  ILogger logger;

  public ILogger Logger { get { return logger; } }

  public void StartLogging(ILogger logger)
  {
    if (logger == null)
      throw new ArgumentNullException("logger");

    this.logger = logger;
  }

  public void StopLogging(bool dispose = true)
  {
    var previousLogger = this.logger as IDisposable;
    this.logger =null;

    if (previousLogger != null && dispose)
      previousLogger.Dispose();
  }

  public void Log(string line)
  {
    if (logger != null) logger.Log(line);
  }

  public void Log(string format, params object[] args)
  {
    if (logger != null) logger.Log(format, args);
  }
}

いくつかの簡単な初期化を行うと:

void InitializeLog()
{
  var log = new ConcurrentLogger(LogToTextBox);
  LogManager.Current.StartLogging(log);
}

void LogToTextBox(string line)
{
  if (!CheckAccess())
  {
    this.Dispatcher.BeginInvoke((Action<string>)LogToTextBox,
                                DispatcherPriority.Background, 
                                line);
    return;
  }

  logTextBox.AppendText(line + Environment.NewLine);
}

次に、コードのどこでも呼び出すことができます:LogManager.Current.Log(...);

于 2013-01-21T18:12:17.023 に答える
0

WriteLineメソッドと、ウィンドウ、コントロール、またはwritelineメソッド内で必要なものを参照するプロパティを保持する静的クラスを作成します。次に、MainWindowコンストラクターまたはロードされたイベントにコードを追加して、Reference-Propertyを必要なアイテムに設定します。その後、どこからでもWritelineを使用できます。

ところで:これは、インスタンスゲッターで静的MainViewModelを使用し、MainWindowのDataContextをこのViewModelにバインドし、MVVMパターンを使用する方がはるかにクリーンな場合があります。その場合、ConsoleOutputプロパティを設定するか、AddLineメソッドまたはコマンドを任意の場所から呼び出すだけで、ビューによってどのように表示されるかを知る必要はありません。単体テストを使用してアプリをテストしたり、視覚的表現を変更したりできます。すべて、アプリケーションのロジックに触れることなく実行できます。

于 2013-01-21T18:00:42.957 に答える
-1
namespace {yourrootnamespace}
{
   namespace GlobalMethods
   {
        static class ConsoleMethods
        {
        mainConsole mc;
        public static WriteLine(string msg, object sender)
        {
            lock (this)
            {
                mc.WriteLine(msg, sender)
            }
        }
        static ConsoleMethods()
        {
            mc = new mainConsole();
        }
        //more methods
    }
}

その後:using {yourrootnamespace}.GlobalMethods;

または、メソッドが mainConsole 引数を取り、それを使用して呼び出すようにします。

于 2013-01-21T17:53:40.033 に答える