C# 値オブジェクトの基底クラス

.Net7 から追加された INumber<T> インターフェースを用いて、
値オブジェクトの基底クラスとなるような record クラスを作成しました。
INumber<T> 型を使用するジェネリッククラスとすることで、
int や double などを場合分けせずに記述することが可能となります。

プリミティブな値型と同じような感覚で扱え、
重要なドメインルールを持つクラスを簡単に作成できるようにすることが目的です。

/// <summary>値の基底クラス</summary>
public abstract record ValueBase<TValue, TSelf>
    where TValue : INumber<TValue>
    where TSelf : ValueBase<TValue, TSelf> {

    private static readonly Func<TValue, TSelf> CreateObject;

    /// <summary>値</summary>
    public TValue Value { get; }

    /// <summary>静的コンストラクタ</summary>
    static ValueBase() {
        var constructorInfo = typeof(TSelf).GetConstructor([typeof(TValue)])
                           ?? throw new NotSupportedException();
        CreateObject = v => (TSelf)constructorInfo.Invoke([v]);
    }

    /// <summary>コンストラクタ</summary>
    protected ValueBase(TValue value) {
        Value = value;
    }

    /// <summary>暗黙的型変換</summary>
    public static implicit operator TValue(ValueBase<TValue, TSelf> valueObject) {
        return valueObject.Value;
    }

    /// <summary>+オペレーター</summary>
    public static TSelf operator +(ValueBase<TValue, TSelf> lhs, ValueBase<TValue, TSelf> rhs) {
        return CreateObject(lhs.Value + rhs.Value);
    }

    /// <summary>ToString</summary>
    public sealed override string ToString() {
        return Value.ToString()!;
    }
}

静的コンストラクタでは、リフレクションを用いてTValue型を引数にとり
継承先のオブジェクトを生成する関数を作成しています。

この例では四則演算のうち加算のみ実装しています。
値オブジェクトから数値への暗黙変換を許可しています。
またrecord 型の継承先クラスではコンパイラにより ToString() が自動でオーバーライドされるので、
数値のみが出力されるように ToString() を sealed にしています。

使用する側は以下のようにします。

/// <summary>得点クラス</summary>
public sealed record Score : ValueBase<int, Score> {
    public Score(int value) : base(value) {}
}

// 使用例
Score score10 = new Score(10);
Score score20 = new Score(20);
Score total = score10 + score20;
Console.WriteLine(total);    // 30 が出力される

 


			

コメントを残す