3

アプローチ方法がわからないという問題があります。ここにいる人々が何か良いヒントを持っていることを願っています。

複数のログ (1 行に 1 つのログ) を含むテキスト ファイルを解析しています。フォーマットは次のようなものです。

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

ここでは、ログには 3 つの「タイプ」(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 { }

現在、各派生クラスは、「説明」フィールドの解析方法に応じて独自の固有のプロパティを持つことができ、3 つの「コア」プロパティ (Date、Type、および Description) を維持します。

ログ ファイルから行を解析するまで、必要な (派生) ログの種類がわからないことを除けば、これまでのところすべて問題ありません。もちろん、行を解析してそれを理解することもできますが、解析コードを「ログ」コンストラクターに入れたいと思っています私はこのようなことができたらいいのにと思います:

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 をログを返す静的に変更します。

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

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 に答える
1

さらに別の解決策。OO ハンマーを下に置き、機能するものを手に取ります。

C# には辞書と無名関数があります。Logと説明を取得する方法を知っていて、その情報を解析して に入れることができる関数の辞書を用意しLogます。その後、あなただけparseDescription[logType](this, description)

これは、3 つの新しいクラスではなく、3 つの関数を持つ辞書が必要であることを意味します。

この例では、違いはそれほど大きくありません。ただし、複数の方法で解析する必要がある可能性があるログ エントリに 2 つの異なるフィールドがあるかどうかを検討してください。クラスベースのアプローチには 9 つの新しいクラスが必要ですが、辞書のアプローチにはそれぞれ 3 つの関数を持つ 2 つの辞書があります。3 つある場合、比較は 27 クラスと、それぞれ 3 つの関数を持つ 3 つの辞書になります。

于 2011-06-21T01:03:57.647 に答える
1

あなたのようにスプリットで行を読んでから、「タイプ」を読んでアクティベーターを呼び出して、(おそらく抽象的な)ベースログから派生した具象タイプの1つを作成し、スプリット引数をコンストラクターに渡して新しい特定の具体的なインスタンス。

(また、インスタンス タイプに基づいて値がわかっているため、「タイプ」は派生クラスの読み取り専用プロパティになる可能性があります)。

もちろん、リフレクションを避けたくないという前提で。

于 2011-06-21T00:47:22.457 に答える