重铸到派生类型

时间:2011-06-21 00:39:53

标签: c# inheritance derived-class base-class

我有一个问题,我不确定如何接近,我希望这里的人会有一些好的提示。

我正在解析包含多个日志(每行一个日志)的文本文件。格式如下:

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();

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

有什么建议吗?

5 个答案:

答案 0 :(得分: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; } }

答案 1 :(得分: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;
}

答案 2 :(得分:1)

你可以像拆分那样读取它中的行,然后读取“type”并调用Activator来创建一个从你的(可能是抽象的)基本日志派生的具体类型,传入你的将构造函数的参数拆分为创建新的特定具体实例。

(另外,“Type”可能是派生类中的只读属性,因为您根据实例类型知道了值。)

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

答案 3 :(得分:1)

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

C#具有词典和匿名功能。有一个函数字典,知道如何获取Log和描述,并可以解析该信息并将其放入Log。然后你只需parseDescription[logType](this, description)

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

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

答案 4 :(得分: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; } }