【C#】ジェネリックのPitfall/小技
Pitfall
ジェネリックの基本については前回までに述べた通りですが、静的メンバーとの兼ね合いでpitfallにはまったことがあります。
例を示します。
Class TestClass<GenericType>
{
public static GenericType Prop { get; set; }
}
TestClass<int>.Prop = 1;
というコードがあった場合、TestClass<string>.Propを取得しようとした場合の結果はどうなるでしょうか?
…
これについては悩むことは少ないと思います。
TestClass<string>.Propはstring型であり、これまで全く触れていません。
つまり未初期化状態、nullということになります。
では次はどうでしょうか?
Class TestClass<GenericType>
{
public static bool Prop { get; set; } = false;
}
TestClass<int>.Prop = true;
というコードがあった場合、TestClass<string>.Propを取得しようとした場合の結果はどうでしょうか?
…
trueと答えた方、見事に穴に落ちました。
正解はfalseです。上の例と同じで、TestClass<string>.Propには触れていないため、初期値のfalseのままとなっています。
静的メンバーはインスタンスを超えて情報を共有できるため便利なのですが、ジェネリッククラスではジェネリックの型指定が異なるものとは共有できないのです。
私は以前このことに気づかず、デバッグに時間がかかってしまったことがありました。
「ジェネリッククラスはジェネリック型指定も含めて一つの型となる」、と認識することが重要です。
小技
さて、ここからが小技です。
基本的にあるクラスの静的メンバーはその派生型も含めて内容が共有されてしまいます。
しかし、場合によってはReflection用などで各々のクラスで内容の異なる同名の静的メンバーを設定したいこともあると思います。
一気に自分でコードを書くのであれば問題ないのですが、チームで作業をしていたり、自分でも間が空いてしまった時にうっかり実装を忘れてしまうことがあるかと思います。
そこで、基底クラスをあえてジェネリックにしてしまうことで静的メンバーを強制的に分離実装することが可能になります。
Class BaseClass<GenericType>
{
public static bool Prop { get; set; } = false;
}
class DerivedClass1 : BaseClass<DerivedClass1> { }
class DerivedClass2 : BaseClass<DerivedClass2> { }
とすると、DerivedClass1.PropとDerived Class2.Propの値は独立となります。
最近はインターフェースに静的メンバーを宣言できるようになったのでこの技は不要なことも多くなったかとは思いますが、古い環境では有用かと思います。