我有一个问题,我不确定如何接近,我希望这里的人会有一些好的提示。
我正在解析包含多个日志(每行一个日志)的文本文件。格式如下:
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。”时所做的所有解析。
有什么建议吗?
答案 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; } }