3

我有一个问题,我不知道如何解决,我希望这里的人会有一些好的建议。

我正在解析文本文件,其中包含多个日志(每行一个日志)。格式类似于以下内容:

Date Type Description
10/20 A LogTypeADescription
10/20 B LogTypeBDescription
10/20 C LogTypeCDescription

在这里,您可以看到日志的三种“类型”(A、B 和 C)。根据日志的类型,我会以不同的方式解析“描述”字段。

我的问题是我应该如何设置数据结构?我想做这样的事情:

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

  public Log(String line)
  {
      Parse(line);
  }
}

class ALog : Log { }
class BLog : Log { }
class CLog : Log { }

现在,每个派生类都可以拥有自己独特的属性,具体取决于“描述”字段的解析方式,并且它们仍将保持三个“核心”属性(日期、类型和描述)。

到目前为止一切都很好,除了我不知道我需要什么类型的(派生)日志,直到我解析了日志文件中的行。当然,我可以解析该行,然后弄清楚,但我真的希望解析代码在“Log”构造函数中。我希望我能做这样的事情:

void Parse(String line)
{
   String[] pieces = line.Split(' ');
   this.Date = DateTime.Parse(pieces[0]);
   this.Type = pieces[1];
   this.Description = pieces[2];

   if(this.Type == "A")
     this = new ALog();
   else if(this.Type == "B")
     this = new BLog();
   else if(this.Type == "C")
     this = new CLog();
}

但不幸的是,我认为这是不可能的。我还没有尝试过,但我很确定这样做:

Log l = new Log(line);
if(l.Type == "A") l = new ALog();

要么是非法的,要么会破坏我第一次创建“日志”时所做的所有解析。

有什么建议么?

4

5 回答 5

2

我会将描述解析分解为一个抽象方法,该方法可以被不同的描述类型覆盖。只有描述有所不同,所以只有这部分行解析需要分解到派生类型中包含的逻辑中。

using System;

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

    public Log(String line)
    {
        String[] pieces = line.Split(' ');
        this.Date = DateTime.Parse(pieces[0]);
        this.Type = pieces[1];
        LogParser parser = GetParser(this.Type);
        this.Description = parser.Parse(pieces[2]);
    }

    static LogParser GetParser(string type)
    {
        switch (type)
        {
            case "A":
                return new AParser();
            case "B":
                return new BParser();
            case "C":
                return new CParser();
            default:
                throw new NotSupportedException();
        }
    }
}

abstract class LogParser { public abstract string Parse(string line);}

class AParser : LogParser { public override string Parse(string line) { /* do parsing for A */ return string.Empty; } }
class BParser : LogParser { public override string Parse(string line) { /* do parsing for B */ return string.Empty; } }
class CParser : LogParser { public override string Parse(string line) { /* do parsing for C */ return string.Empty; } }
于 2011-06-21T00:49:48.127 回答
2

删除构造函数并将 Parse 更改为返回 Log 的静态。

static Log Parse(string line)
{
     string[] tokens  line.Split(' ');
     var log = null;
     if (tokens[1] == "A") log = new ALog();
     else if (tokens[1] == "B") log = new BLog();
     else log = new CLog();
     log.Date = tokens[0];
     log.Description = tokens[1];
     return log;
}
于 2011-06-21T00:50:38.267 回答
1

您可以像您一样阅读拆分中的行,然后阅读“类型”并调用激活器以创建从(可能是抽象的)基本日志派生的具体类型之一,将拆分参数传递给构造函数,创建一个新的具体实例。

(此外,“类型”可能是派生类中的只读属性,因为您知道基于实例类型的值)。

当然,假设您不想避免反思。

于 2011-06-21T00:47:22.457 回答
1

又一个解决方案。放下你的 OO 锤,拿起你的功能锤。

C# 有字典和匿名函数。有一个函数字典,知道如何获取 aLog和一个描述,并且可以解析该信息并将其放入Log. 那你就parseDescription[logType](this, description)

这意味着您需要一个包含 3 个函数而不是 3 个新类的字典。

在这个例子中差异不是很大。但是请考虑您的日志条目中是否有 2 个不同的字段可能需要以多种方式解析。基于类的方法需要 9 个新类,而字典方法有 2 个字典,每个字典有 3 个函数。如果有 3 个,则比较将是 27 个类与 3 个字典,每个字典具有 3 个函数。

于 2011-06-21T01:03:57.647 回答
1

重新审视 joncham 和 btilly 的方法:

using System;

class Log
{
    DateTime Date;
    String Type;
    String Description;
    Dictionary<string,LogParser> logDictionary;

    static Log()
    {
        logDictionary = new Dictionary<string,LogParser>;

        logDictionary.Add("A",new AParser());
        logDictionary.Add("B",new BParser());
        logDictionary.Add("C",new CParser());
    }

    public Log(String line)
    {
        String[] pieces = line.Split(' ');    
        this.Date = DateTime.Parse(pieces[0]);    
        this.Type = pieces[1];
        LogParser parser = GetParser(this.Type);
        this.Description = parser.Parse(pieces[2]);
    }

    static LogParser GetParser(string type)
    {
        return logDictionary<string,LogParser>(type);
    }
}

abstract class LogParser { public abstract string Parse(string line);}

class AParser : LogParser { public override string Parse(string line) { /* do parsing for A */ return string.Empty; } }
class BParser : LogParser { public override string Parse(string line) { /* do parsing for B */ return string.Empty; } }
class CParser : LogParser { public override string Parse(string line) { /* do parsing for C */ return string.Empty; } }
于 2011-06-21T01:07:17.953 回答