web-dev-qa-db-ja.com

`enum`をマルチタイプソリューションで置き換えるにはどのようなオプションがありますか?

簡単なメモ

質問のタイトルが少し変わっていることは知っていますが、今朝はカフェインをあまり摂っていないので、今ははっきりと考えるのに苦労しています。より良いタイトルについての提案があれば、以下にコメントしてください。更新します。


問題

私(私の組織)がアプリケーション内のイベントをログに記録できるカスタムロギングソリューションを構築しました。ソリューションでは、イベントをログに記録するときにイベントタイプが必要です。アプリケーション固有のイベントタイプのサポートを追加したいと思います。

現在、すべてのイベントタイプを含む単一のenumがあります。各アプリケーションに関連する特定のイベントタイプを追加すると、enumで使用できる値の数が大幅に増えるため、これは適切な方法ではないと思います。たとえば、2つのアプリケーションのみのサポートが追加されたため、すでに最大22の異なるイベントタイプがあります。

私の現在の考え

さまざまなイベントタイプの複数のenum宣言を含む単一のクラスを作成することを考えましたが、すべての異なるenumsを処理する単一のメソッドを作成できず、複数を作成したくないので、これは機能しませんそれぞれのenumのメソッドはメンテナンスの悪夢になるためです。

// Simple example.
public bool LogEvent(EventType type) { /* ... */ }

考えられる解決策は、次のようないくつかのconst宣言を含むクラスのコレクションを作成することです。

public sealed class EventTypes {
    public class EventType {
        public string Name { get; private set; }
        public EventType(string name) { Name = name; }
    }
    public class Validation : EventType {
        public const int GeneralValidation = 100;
        public const int DataValidation = 101;
        public const int UserValidation = 102;
        private Validation() : base("Validation") { }
    }
    public class Application1 : EventType {
        public const int Event1 = 1000;
        public const int Event2 = 1001;
        private Application1() : base("Application1") { }
    }
}

ただし、これがオブジェクト指向環境でのベストプラクティスに従うアプローチであり、「hacky」のような感じがするかどうかはよくわかりません。また、クラスがEventTypesコンテナークラスの外部でインスタンス化されないようにし(abstractでもプライベートコンストラクターを実行できますか?)、EventTypesは継承できないため、sealedはそこに適用できますが、そのいずれか。

必要条件

私が探しているソリューションでは、現在のようにイベントをログに記録するための単一の方法を使用できます。 LogEventメソッドを呼び出すときの実装例は、次のようになります。

LogEvent(EventTypes.ApplicationName.EventTypeName);
LogEvent(EventTypes.Validation.DataValidation);

主な目標は、クエリとビューでレコードが失われないようにするために、データに一貫性を持たせることです。たとえば、次のように矛盾が生じる可能性があるため、アプリケーション名を手動で入力することを避けたいと思います。

LogEvent("Application Name", "Event Name");
LogEvent("App Name", "Evt Name");

確かに、これを防ぐために定数を使用できますが、開発者は定数を使用する必要があり、標準に準拠していることを確認するためにコードレビューが必要です。無効なタイプが指定された場合、理想的なソリューションはコンパイルの失敗を引き起こします。

質問

これはおそらく最良の表現ではありません。ただし、単一のenumを、複数のタイプをサポートし、オブジェクト指向環境でのベストプラクティスに従うソリューションで置き換えるには、どのオプションを使用できますか?

2

ロギング用のリポジトリーがあり、どのタイプでもログに記録できる以下のようなメソッドがあります。ログをクエリするためのSearch()およびGetByID()の同様のメソッドがあります。このメソッドを使用すると、ICommandインターフェースを実装する以外に、サポートされているタイプを指定する必要はありません。渡されたオブジェクトはjsonとしてシリアル化され、Search()などのメソッドで逆シリアル化されます。

    public long Log<T>(T command) where T: ICommand {
        var type = GetTypeName<T>();
        var payload = JsonConvert.SerializeObject(command, Formatting.None);
        var entity = new Command { CreatedByID = command.CreatedByID, Payload = payload, Type = type };
        context.Commands.AddObject(entity);
        context.SaveChanges();
        return entity.ID;
    }

    private string GetTypeName<T>() {
        return typeof(T).FullName;
    }
2

あなたが探しているのはタイプセーフな列挙型パターンだと思います。これは、Java enumキーワードが導入される前は、Javaでよく使われていた方法でした。Java = enumの実装は、このパターンに基づいて構築され、言語レベルでのみ実行できるいくつかの機能を追加します。私の理解では、C#には対応する機能がないため、タイプセーフなenumパターンはここで理にかなっています。

以下は、初期コードに基づく例です。

public sealed class EventTypes {
    public class EventType {
        public readonly int Value { get; }
        protected EventType(int value) { Value = value; }
    }
    public sealed class Validation : EventType {
        public static readonly Validation GeneralValidation = new Validation(100);
        public static readonly Validation DataValidation = new Validation(101);
        public static readonly Validation UserValidation = new Validation(102);
        private Validation(int value) : base(value) { }
    }
    public sealed class Application1 : EventType {
        public static readonly Application1 Event1 = new Application1(1000);
        public static readonly Application1 Event2 = new Application1(1001);
        private Application1(int value) : base(value) { }
    }
}

ここで構文に問題がある場合は、お知らせください。

このアプローチについてC#で説明する記事を次に示します。 https://www.infoworld.com/article/3198453/how-to-implement-a-type-safe-enum-pattern-in-c.html

1
JimmyJames

列挙型を許可(System.Enum base type)であり、列挙型と列挙型の値の両方の名前を使用します。

public bool LogEvent(Enum eventType)
{
    Console.WriteLine
    (   
          "An event was logged. Application: {0}, Category: {1} EventType: {2}",
          eventType.GetType().Assembly.FullName,
          eventType.GetType().Name,
          eventType.ToString()
    );
    //etc....
}

このようにして、開発者はイベントに必要な列挙型を自由に定義でき、LogEventメソッドは列挙型がどこから来たか、そしてそれが何を意味するかを理解できます。

0
John Wu