C# 用于在库中解析期间引发错误/警告的模式

C# 用于在库中解析期间引发错误/警告的模式,c#,.net,design-patterns,C#,.net,Design Patterns,我有一个库,库中描述的对象模型有两种输入格式。我目前使用事件订阅模型向库的最终用户发出错误/警告/详细消息。这还没有被证明是最干净的模型,我想知道在.Net框架(在罗马时)中是否有相关的设计模式或类似的东西可以更好地处理这种情况 // Rough outline of the current code public abstract class ModelReader : IDisposable { public abstract Model Read(); public ev

我有一个库,库中描述的对象模型有两种输入格式。我目前使用事件订阅模型向库的最终用户发出错误/警告/详细消息。这还没有被证明是最干净的模型,我想知道在.Net框架(在罗马时)中是否有相关的设计模式或类似的东西可以更好地处理这种情况

// Rough outline of the current code
public abstract class ModelReader : IDisposable
{
    public abstract Model Read();

    public event EventHandler<MessageAvailableEventArgs> MessageAvailable;

    protected virtual void RaiseError(string message)
    {
        var handler = this.MessageAvailable;
        if (handler != null)
        {
            handler(this, new MessageAvailaibleEventArgs(
                TraceEventType.Error, message);
        }
    }
}
//当前代码的大致轮廓
公共抽象类ModelReader:IDisposable
{
公共抽象模型Read();
公共事件事件处理程序消息可用;
受保护的虚拟void RaiseError(字符串消息)
{
var handler=this.MessageAvailable;
if(处理程序!=null)
{
处理程序(此,新MessageAvailableEventArgs(
TraceEventType.错误,消息);
}
}
}

编辑:一些说明。
读取
例程将使用异常在所有致命错误上快速失败。消息将被记录到用户端的潜在多个源,因此任何模式都应避免限制潜在源的数量。

如果存在阻止库执行此操作的错误这是我将使用异常的工作

如果这是严格用于跟踪和检测,我会选择您的模式或TextWriter模式,消费者会传入一个文本编写器,您会将跟踪信息写入其中,唯一的问题是您只能有一个外部订阅者。但这会导致代码稍微干净一些

 public TextWriter Log { get; set; }

 private void WriteToLog(string Message)
 {
    if (Log != null) Log.WriteLine(message);
 }

我认为事件订阅模式是可以的,但是你可以考虑界面,这样可以给你更多的灵活性。

public interface IMessageHandler
{
    void HandleMessage(object sender, MessageAvailaibleEventArgs eventArgs);
}    

public abstract class ModelReader : IDisposable
{
    private readonly IMessageHandler handler; // Should be initialized somewhere, e.g. in constructor

    public abstract Model Read();

    public event EventHandler<MessageAvailableEventArgs> MessageAvailable;

    protected virtual void RaiseError(string message)
    {
        MessageAvailaibleEventArgs eventArgs =
            new MessageAvailaibleEventArgs(TraceEventType.Error, message);
        this.handler.HandleMessage(this, eventArgs);
    }
}
公共接口IMessageHandler
{
void HandleMessage(对象发送方,messageavailableeventargs eventArgs);
}    
公共抽象类ModelReader:IDisposable
{
私有只读IMessageHandler;//应该在某个地方初始化,例如在构造函数中
公共抽象模型Read();
公共事件事件处理程序消息可用;
受保护的虚拟void RaiseError(字符串消息)
{
MessageAvailableEventArgs事件Args=
新MessageAvailableEventArgs(TraceeEventType.Error,message);
this.handler.HandleMessage(this,eventArgs);
}
}
因此,现在您可以使用消息处理程序的任何实现,对于exmaple您的事件订阅:

public class EventMessageHandler : IMessageHandler
{
    public event EventHandler<MessageAvailaibleEventArgs> MessageAvailable;

    public void HandleMessage(object sender, MessageAvailaibleEventArgs eventArgs)
    {
        var handler = this.MessageAvailable;
        if (handler != null)
        {
            handler(this, new MessageAvailaibleEventArgs(
                TraceEventType.Error, message);
        }
    }
}
公共类EventMessageHandler:IMessageHandler
{
公共事件事件处理程序消息可用;
public void HandleMessage(对象发送方,messageavailableeventargs eventArgs)
{
var handler=this.MessageAvailable;
if(处理程序!=null)
{
处理程序(此,新MessageAvailableEventArgs(
TraceEventType.错误,消息);
}
}
}

我可以给你一个真实的例子。这个库是一个解析库。它只是在reader类上定义了一个解析错误列表。扩展你的例子,它会是这样的:

    public abstract class ModelReader
    {
        private List<ParseError> _errors = new List<ParseError>();
        private bool _throwOnError;

        public ModelReader()
           :this(true)
        {
        }

        public ModelReader(bool throwOnError)
        {
           _throwOnError = throwOnError;
        }

        // use AddError in implementation when an error is detected
        public abstract Model Read();

        public virtual IEnumerable<ParseError> Errors
        {
           get {return _errors;}
        }

        protected virtual void AddError(ParseError error)
        {
           if (_throwOnError) // fail fast?
              throw new ParseException(error);

           _errors.Add(error);
        }
    }

    public class ParseError
    {
        public ParseError(...)
        {
        }

        public ParseErrorCode Code { get; private set; }
        public int Line { get; private set; }
        public int LinePosition { get; private set; }
        public string Reason { get; private set; }
        public string SourceText { get; private set; }
        public int StreamPosition { get; private set; }
    }

    public enum ParseErrorCode
    {
       InvalidSyntax,
       ClosingQuoteNotFound,
       ... whatever...
    }

    public class ParseException: Exception
    {
        ...
    }
公共抽象类ModelReader
{
私有列表_errors=新列表();
私人布卢·斯洛沃恩错误;
公共模型阅读器()
:这是真的
{
}
公共模型读取器(bool throwOnError)
{
_throwOnError=throwOnError;
}
//当检测到错误时,在实现中使用加法器
公共抽象模型Read();
公共虚拟可数错误
{
获取{return\u errors;}
}
受保护的虚拟空加法器错误(ParseError错误)
{
如果(\u throwOnError)//快速失败?
抛出新的ParseException(错误);
_错误。添加(错误);
}
}
公共类解析错误
{
公共语法错误(…)
{
}
公共语法错误代码{get;private set;}
公共int行{get;private set;}
public int LinePosition{get;private set;}
公共字符串原因{get;private set;}
公共字符串SourceText{get;private set;}
public int StreamPosition{get;private set;}
}
公共枚举解析错误代码
{
残疾妇女税,
ClosingQuoteNotFound,
无论什么
}
公共类ParseException:异常
{
...
}

如果库调用方想要即时事件,您仍然可以添加事件。

您当前的方法不是最干净的模型,因为它有两种相互矛盾的执行样式:pull(读取输入)和push(处理错误回调),这意味着您的读者和客户都需要更多的状态管理来提供一个连贯的整体


我建议您避免使用类似XmlReader的界面,并使用访问者模式将输入数据和错误推送到客户端应用程序。

您似乎需要一个可组合的验证器,用户可以在其中插入自己的逻辑,将特定的非致命错误标记为致命、警告或信息性消息。引发异常不会如果另一部分想将其转换为警告,但又想继续解析,那么您已经离开了该方法。这听起来很像Windows中用于结构化异常处理的双过程异常处理模型。 基本上是这样的

  • 注册任意数量的异常处理程序
  • 当在pass中检测到问题时,会询问所有处理程序(还没有抛出exeption!)哪一个要处理错误。第一个说是的处理程序将成为实际的处理程序,该处理程序决定要做什么
  • 当找到一个可以处理的处理程序时,我们通过2调用它。这一次是异常抛出时间还是不抛出时间。这完全取决于处理程序
  • 这个双过程模型的优点在于,在第一个过程中,会询问所有处理程序,但您仍然能够继续解析
        void Read(Func<string, bool> WarningHandler)
        {
            bool cancel = false;
            if (HasAWarning)
                cancel = WarningHandler("Warning!");
        }