正在重新投射到派生类型

本文关键字:派生 类型 新投射 | 更新日期: 2023-09-27 17:59:51

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

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

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

在这里,您可以看到有三种"类型"的日志(A、B和C)。根据日志的类型,我将以不同的方式解析"Description"字段。

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

class Log
{
  DateTime Date;
  String Type;
  String Description;
  public Log(String line)
  {
      Parse(line);
  }
}
class ALog : Log { }
class BLog : Log { }
class CLog : Log { }

现在,每个派生类都可以有自己独特的属性,这取决于"Description"字段的解析方式,并且它们仍将维护三个"核心"属性(Date、Type和Description)。

到目前为止一切都很好,只是我不知道我需要什么类型的(派生的)日志,直到我解析了日志文件中的行。当然,我可以解析这一行,然后计算出来,但我真的希望解析代码在"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();

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

有什么建议吗?

正在重新投射到派生类型

删除构造函数并将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;
}

我会将描述解析考虑到一个抽象方法中,该方法可以针对不同的描述类型进行重写。只有描述不同,所以只有这部分行解析需要分解到派生类型中包含的逻辑中。

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; } }

您可以像现在这样读取拆分中的行,然后读取"type"并调用Activator来创建从(可能是抽象的)基日志派生的一个具体类型,将拆分参数传递给构造函数,创建一个新的特定具体实例。

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

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

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

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

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

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

重新审视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; } }