概要
とあるクラスから、そのクラスのプロパティに保持しておいた別のクラスを呼び出す際に、プロパティに保持したいクラスが2種類以上ある場合で、少しだけ工夫した書き方をまとめてみました。
説明
とあるクラスのプロパティに別のクラスを保持したいとき、その「別のクラス」が2種類以上あり、また保持したいクラスはその候補のうちの1種類だけ、という場合に、インターフェースを使用して1つのプロパティのみで保持できる、という書き方になります。
コード
Child
という名前のクラスがあり、このクラスのプロパティにParent1
クラス、もしくはParent2
クラスのいずれかを保持したい場合、という形で実装していきます。
まずは下記のようにinterfaceを定義します。
public interface IParent { }
Parent1
クラスとParent2
クラスに先ほどのインターフェースを実装させます。
この2つのクラスはプロパティに、Child
クラスのリストを保持しています。
public class Parent1 : IParent
{
public List<Child> Children { get; } = new List<Child>();
public void CreateChildren()
{
// サンプルとして、子を一つ追加
var child = new Child(this);
Children.Add(child);
// その他の処理
}
}
public class Parent2 : IParent
{
public List<Child> Children { get; } = new List<Child>();
public void CreateChildren()
{
// サンプルとして、子を一つ追加
var child = new Child(this);
Children.Add(child);
// その他の処理
}
}
Child
クラスのコンストラクタでインターフェース型の引数を受け取り、プロパティに保持します。
public class Child
{
/// <summary>
/// 親
/// </summary>
public IParent Parent;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="parent"></param>
public Child(IParent parent)
{
Parent = parent;
}
}
大まかな実装は以上です。
以下から、使用方法について記載します。
使用方法
例えばインターフェースに、Parent1
とParent2
のどちらのクラスにも実装しているメソッドを記載しておけば、Child
クラスからプロパティに保持されたParentクラスの種類に応じて、そのクラスのメソッドを呼び出すことができます。
例えば、WPFになりますが私がよく使用していた方法だと、インターフェースを下記のように定義し、
public interface IParent
{
/// <summary>
/// 子のプロパティが変わったときに呼ばれる
/// </summary>
/// <param name="child"></param>
void ChildChanged(Child child);
}
Parent1
とParent2
にインターフェースで定義したChildChanged
を実装します。
public class Parent1 : IParent
{
public List<Child> Children { get; } = new List<Child>();
public void CreateChildren()
{
// サンプルとして、子を一つ追加
var child = new Child(this);
Children.Add(child);
// その他の処理
}
/// <summary>
/// 子の特定のプロパティが変わったときに呼ばれる
/// </summary>
/// <param name="child"></param>
public void ChildChanged(Child child)
{
// 処理
}
}
public class Parent2 : IParent
{
public List<Child> Children { get; } = new List<Child>();
public void CreateChildren()
{
// サンプルとして、子を一つ追加
var child = new Child(this);
Children.Add(child);
// その他の処理
}
/// <summary>
/// 子の特定のプロパティが変わったときに呼ばれる
/// </summary>
/// <param name="child"></param>
public void ChildChanged(Child child)
{
// 処理
}
}
Child
クラスで、prismのOnPropertyChanged()
を実装し、その中で親のChildChanged()
を呼び出します。
そうすることで、Child
クラスのプロパティが変化した場合、自動で紐づく親クラス (ここではparent1
もしくはparent2
) のChildChanged()
が呼び出され、Child
クラスのプロパティの変化に自動で応対する形でParent
クラスの処理を実行することができます。
public class Child : BindableBase
{
/// <summary>
/// 親
/// </summary>
public IParent Parent;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="parent"></param>
public Child(IParent parent)
{
Parent = parent;
}
public bool PropertyA { get => _propertyA; set => SetProperty(ref _propertyA, value); }
private bool _propertyA;
public bool PropertyB { get => _propertyB; set => SetProperty(ref _propertyB, value); }
private bool _propertyB;
public bool PropertyC { get => _propertyC; set => SetProperty(ref _propertyC, value); }
private bool _propertyC;
protected override void OnPropertyChanged(PropertyChangedEventArgs args)
{
try
{
if (string.IsNullOrEmpty(args.PropertyName)
|| args.PropertyName == nameof(PropertyA)
|| args.PropertyName == nameof(PropertyB))
{
// PropertyAとPropertyBが変わった場合のみ、親に伝える
Parent.ChildChanged(this);
}
}
catch (Exception ex)
{
//エラー処理
}
}
}
Child
クラスが継承しているBindableBase
クラスや、SetProperty
、OnPropertyChanged
はWPFでよく使用されるライブラリprism
の機能です。
これらの機能を使用し上記のように書くと、プロパティが変更された際に、OnPropertyChanged
メソッドが自動で走るようになります。
その処理内で、Parentクラスに通知している形になります。
上記以外にも、以下のようにChild
クラス内でキャストすることで、キャストされたParent
クラスが使用できます。
例えば、Parent1
クラスとParent2
クラスで別々のプロパティがあり、
public class Parent1 : IParent
{
public List<Child> Children { get; } = new List<Child>();
/// <summary>
/// Parent1クラスのみにあるプロパティ
/// </summary>
public string Parent1Property {get; set; }
public void CreateChildren()
{
// サンプルとして、子を一つ追加
var child = new Child(this);
Children.Add(child);
// その他の処理
}
}
public class Parent2 : IParent
{
public List<Child> Children { get; } = new List<Child>();
/// <summary>
/// Parent2クラスのみにあるプロパティ
/// </summary>
public bool Parent2Property {get; set; }
public void CreateChildren()
{
// サンプルとして、子を一つ追加
var child = new Child(this);
Children.Add(child);
// その他の処理
}
}
以下のように、それぞれのクラスにキャストをすると、キャスト後のクラスが使用できます。
public class Child
{
/// <summary>
/// 親
/// </summary>
public IParent Parent;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="parent"></param>
public Child(IParent parent)
{
Parent = parent;
}
}
public void SampleMethod()
{
if (Parent is Parent1 parent1)
{
// プロパティのクラスがParent1だった場合の処理
parent1.Parent1Property = "ほげほげ";
}
if (Parent is Parent2 parent2)
{
// プロパティのクラスがParent2だった場合の処理
parent2.Parent2Property = true;
}
}
終わりに
書きたかったことはコードに書いたとおりのことなのですが、記事のタイトルや概要、説明の文章ににすごく悩みました。
コードでやりたい思いやイメージを言葉で表現するのは難しい場合もあると感じました。