【问题标题】:Inheritance and Multiple Constructors继承和多个构造函数
【发布时间】:2011-07-06 13:23:15
【问题描述】:

我有一个关于继承的问题,所以我将描述下面的场景:

我正在阅读一个包含日志的文本文件。 (每行一个日志) 每个日志行将具有以下格式: "日期类型说明"

但是,根据日志的“类型”,我将不得不以不同的方式解析“描述”并提取不同的字段。

以下是一些示例:

5/1/2011 Information Field1, Field2, Field3
5/2/2011 Error       Field1

-- 所以,我试图做的是:
- 从日志中取出一行
-根据模式“日期类型描述”解析它
-查看“类型”字段,并根据需要创建新对象/解析描述

public class Log
{
   public DateTime Date;
   public String Type;
   public String Description;

   public Log(String line)
   {
      this.Date = GetDate();
      this.Type = GetType();
      this.Description = GetDescription();
   }
}

public class InformationLog : Log
{
   public String Field1;
   public String Field2;
   public String Field3;

   public InformationLog(Log log)
   {
      this.Field1 = GetField1(log.Description);
      this.Field1 = GetField2(log.Description);
      this.Field1 = GetField3(log.Description);
   }
}

public class Client
{
   public void Main()
   {
       String line = ReadFileAndGetLine();  // Get a line from the file
       Log log = new Log(line);
       if(log.Type == "Information")
          log = new InformationLog(log);    // Is this right?
   }
}

这可以按我的意愿工作,但似乎这不是一个好习惯。 “log”变量将自己用作自己构造函数的参数。

我的问题是: 有这样做的标准方法吗?或者,这个实现有什么问题吗?

--
编辑:
另外,我应该提一下:我的理由是我会解析该行以获取日期和类型,然后再次解析以获取更详细的信息。
我决定使用继承,这样我就不必两次解析 Date 和 Type 字段。

【问题讨论】:

  • 为什么不直接使用Log进行条件解析?
  • @Mr.失望:每种类型的日志都有不同的参数,所以我不想放“Field1,Field2,Field3等”。 “日志”中的对象(如果不需要)。
  • 赞赏,但如果它们总是包含Type 和Description,那么这仍然可以在基类中发生。
  • 你不能做得很好,因为你每个新的日志扩展都会提供自己拥有的字段。在您的示例中,您无法获取 FieldX 值,因为“log”是基本类型。
  • 你试过log4net吗?它是一个很棒的第 3 方日志记录工具。

标签: c# .net inheritance multiple-inheritance


【解决方案1】:

尝试使用Factory pattern

static class LogFactory
{
    public static Log Create(String line)
    {
        if(GetType(line) == "Information")
           return CreateInformationLog(line);
        return CreateLog(line);
    }

    private static Log CreateLog(String line)
    {
       return new Log(line);
    }

    private static Log CreateInformationLog(String line)
    {
       return new InformationLog(line);
    }
}

然后尝试使用

   String line = ReadFileAndGetLine();  // Get a line from the file
   Log log = LogFactory.Create(line);

【讨论】:

  • 谢谢。有没有办法保留“日期”和“类型”字段而不必调用“GetType(line)”两次? InformationLog(line) 的构造函数似乎必须再次调用 GetType(line)。
  • 你可以将构造函数扩展为Log(String line,string type)
【解决方案2】:

根据我的评论,为什么不做一些这样的事情:

    public enum LogEntryType
    {
        Error = -1,
        Information = 0,
    }

    public class LogEntry
    {
        public string Raw;
        public DateTime Date;
        public LogEntryType Type;
        public string Description;

        public LogEntry(String line)
        {
            Raw = line;
            Date = ParseDate();
            Type = ParseType();
            Description = ParseDescription();
        }

        public string ParseDescription()
        {
           var result = string.Empty;
           switch(Type)
           {
               case LogEntryType.Error:
                   //parse here
                   break;
               case LogEntryType.Information:
                   //parse here
                   break;
           }
           return result;
        }
    }

我注意到您在派生类中有字段,但可以在此处解析描述;不过,我可以理解为什么人们可能希望将其转移到真正知道应该如何解析描述的地方,在这种情况下,您可以使用另一个答案中建议的工厂模式,或者实现一个 ' property bag' 类型的场景 - 但我认为这些天通常不赞成使用强类型。

另一个建议,虽然与您最初的尝试非常相似,但倾向于封装类型的管理,而不是让一个分离的类处理这些东西 - 一个有点(表面上)像 Exception 的模式,你有一个根条目和内部条目:

    public enum LogEntryType
    {
        Error = -1,
        Information = 0,
    }

    public class LogEntry
    {
        public string Raw;
        public DateTime Date;
        public LogEntryType Type;
        public string Description;

        public InnerLogEntry InnerEntry;

        public LogEntry(String line)
        {
            Raw = line;
            Date = ParseDate();
            Type = ParseType();
            //parse the 'raw' description...
            Description = ParseDescription();
            //determine the inner entry type...
            switch (Type)
            {
                case LogEntryType.Error:
                    InnerEntry = new ErrorLogEntry(this);
                    break;
                case LogEntryType.Information:
                    InnerEntry = new InformationLogEntry(this);
                    break;
            }                
        }
    }

    public abstract class InnerLogEntry
    {
        protected LogEntry Parent;

        public InnerLogEntry(LogEntry logEntry)
        {
            Parent = logEntry;
        }
    }

    public class InformationLogEntry : InnerLogEntry
    {
        public InformationLogEntry(LogEntry logEntry)
            : base(logEntry)
        {
            //parse custom data
        }
    }

    public class ErrorLogEntry : InnerLogEntry
    {
        public ErrorLogEntry(LogEntry logEntry)
            : base(logEntry)
        {
            //parse custom data
        }
    }

【讨论】:

  • 谢谢,但是,我不知道如何存储“Field1、Field2 等”。参数? --“Description”可能包含3个字符串,也可能有一个DateTime和一个Double等
  • 我更喜欢你的第二次尝试。对我来说,实际上看起来像一个抽象工厂。我认为这是我想要的更多方向。谢谢。
  • 没问题,我认为它非常适合某些情况——特别是如果它适合你的话。您还可以让InnerLogEntry 派生自LogEntry,这样每个条目都可以有一组基本数据,和/或在必要时拥有自己的孩子——这需要更多的调整,但只是一个想法。
猜你喜欢
  • 2014-11-22
  • 2011-09-11
  • 1970-01-01
  • 2016-03-24
  • 2012-04-24
  • 2011-12-27
  • 2017-11-17
  • 2020-08-07
相关资源
最近更新 更多