C# 你能让所有线程中的所有代码都能访问对象的方法吗?

C# 你能让所有线程中的所有代码都能访问对象的方法吗?,c#,wpf,multithreading,user-interface,C#,Wpf,Multithreading,User Interface,感谢大家对这个问题的关注。你们中的一些人要求对所涉及的代码更加清晰,所以为了提供更多的信息,我将对其进行编辑以提供更多的细节 关于,我试图在WPF窗口中模拟一个基本控制台(仅限文本输出)。这意味着要与一个程序一起工作,该程序有很多代码在后台运行,在不同的线程上运行。这段代码也严重依赖while循环,因此我的计划是将WPF控制台窗口保持在主线程上(以及可能需要的任何其他GUI窗口),并在单独的线程上执行所有代码 该窗口使用的WriteLine方法如下: mainConsole.WriteLine(

感谢大家对这个问题的关注。你们中的一些人要求对所涉及的代码更加清晰,所以为了提供更多的信息,我将对其进行编辑以提供更多的细节

关于,我试图在WPF窗口中模拟一个基本控制台(仅限文本输出)。这意味着要与一个程序一起工作,该程序有很多代码在后台运行,在不同的线程上运行。这段代码也严重依赖while循环,因此我的计划是将WPF控制台窗口保持在主线程上(以及可能需要的任何其他GUI窗口),并在单独的线程上执行所有代码

该窗口使用的WriteLine方法如下:

mainConsole.WriteLine("This is a message for the user.", SomeSender);
代码的其余部分将需要定期调用此方法

其他信息:

窗口本身由一个包装在滚动条中的文本块组成。窗口的WriteLine方法将消息和格式(字体、字体大小和颜色-取决于消息的发件人)添加到包含此信息的对象列表中,然后将这些消息的列表(包括其格式)显示为文本块的内容。该方法完全按照预期工作,因此不需要重新编写,只需要可访问

我已经尽力使这个描述尽可能简洁。有关更多信息,请参阅

因此,我现在的问题是:是否有一种有效的方法使窗口的WriteLine方法可用于任何类的所有线程,从而使我能够像Console.WriteLine()一样使用它?

然后:
使用{yourrootnamespace}.GlobalMethods


或者,让这些方法接受一个mainConsole参数,然后使用它来调用。

虽然您有许多选项,但在您的情况下,任何地方的任何人都可以对您的控制台进行写入,这听起来确实很有意义。鉴于此,我将创建如下内容:

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");
    }
}
公共类MyConsole
{
公共静态事件动作文本;
公共静态无效写入(对象obj)
{
字符串文本=(obj???).ToString();
if(textWrited!=null)
文本书写(文本);
}
公共静态无效写入线(对象obj)
{
写入(obj+“\n”);
}
}
然后让您的控制台窗体订阅
textWrited
事件,并在写入文本时将该文本写入控制台。(请确保首先封送到UI线程。)


在这里使用and event的主要优点是,与让这个类直接处理表单不同,您可以轻松地添加额外的事件处理程序,允许您与标准输入/输出交互,向文件添加额外的日志记录,同时打开多个控制台表单,这种灵活性对于调试(即对平面文件的附加写出)和生产(允许通过标准输入/输出更轻松地重定向)都很有用。

创建一个静态类,该类包含WriteLine方法和引用窗口、控件或WriteLine方法内部所需的任何内容的属性。 然后向MainWindow构造函数或加载的事件添加一些代码,以将引用属性设置为所需的项。 之后,您可以在任何地方使用Writeline


顺便说一句:使用带有实例getter的静态MainViewModel,将MainWindow的DataContext绑定到此ViewModel,并使用MVVM模式,这可能会更干净。然后,您只需要设置一些ConsoleOutput属性,或者从任何您想要的位置调用AddLine方法甚至命令,而不必知道视图如何显示它。您可以使用单元测试来测试您的应用程序,您可以更改视觉表示。。。所有这些都不涉及应用程序的逻辑。

您似乎正在尝试编写一个日志服务,允许您从代码中的任何位置访问日志。您提到线程,因此您必须注意并相应地处理该同步

我首先创建一个
ILogger
界面,如下所示:

public interface ILogger
{
  void Log(string line);
  void Log(string format, params object[] args);
}
然后是适当的
记录器
基类:

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);
  }
}
通过一些快速初始化:

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);
}
void InitializeLog()
{
var log=新的ConcurrentLogger(LogToTextBox);
LogManager.Current.StartLogging(日志);
}
无效LogToTextBox(字符串行)
{
如果(!CheckAccess())
{
此.Dispatcher.BeginInvoke((操作)LogToTextBox,
DispatcherPriority.背景,
线路);
返回;
}
logTextBox.AppendText(line+Environment.NewLine);
}

然后在代码中的任何地方都可以调用:
LogManager.Current.Log(…)

如果不知道
WriteLine
的方法体访问了哪些共享项,则无法确定。提供该实现可能是一个想法。@spender-OP询问如何使其可访问,而不是如何实现它…以及该静态方法如何访问
mainConsole
?@您需要创建并维护
mainConsole
的实例才能访问它。这可能很棘手,因为它可能需要在某个时间点创建。@ofstream它可以访问该表单的类型,但不能访问相关的实例,因此,不,在同一命名空间中是不够的。然后还要添加一些缩进。这在多线程环境中不起作用,这就是OP说他将在这个系统中使用它的内容,为什么?注意,我特别声明事件接收器应该封送到UI线程,UI线程将同步所有事件。如果需要,任何其他事件处理程序也将负责同步它们的工作(有些可能不需要)。编组到UI线程可能是线程安全的,但是
调用
将阻止正在运行的线程。在高UI负载的情况下,这些线程的性能将受到很大影响。美国
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);
}