C# でオブジェクトが辞書であるかどうかをテストする
-
02-07-2019 - |
質問
オブジェクトが辞書であるかどうかをテストする方法はありますか?
メソッドでは、リスト ボックスで選択した項目から値を取得しようとしています。状況によっては、リスト ボックスが辞書にバインドされる場合がありますが、これはコンパイル時にはわかりません。
これと同様のことをしたいと思います。
if (listBox.ItemsSource is Dictionary<??>)
{
KeyValuePair<??> pair = (KeyValuePair<??>)listBox.SelectedItem;
object value = pair.Value;
}
リフレクションを使用して実行時にこれを動的に行う方法はありますか?ジェネリック型でリフレクションを使用してキー/値パラメーターを決定できることはわかっていますが、それらの値を取得した後に残りを行う方法があるかどうかはわかりません。
解決
次のようなものになるはずです。これは回答欄に書いたので構文が正確ではないかもしれませんが、誰でも修正できるように Wiki で編集できるようにしました。
if (listBox.ItemsSource.IsGenericType &&
typeof(IDictionary<,>).IsAssignableFrom(listBox.ItemsSource.GetGenericTypeDefinition()))
{
var method = typeof(KeyValuePair<,>).GetProperty("Value").GetGetMethod();
var item = method.Invoke(listBox.SelectedItem, null);
}
他のヒント
IDictionary が実装されているかどうかを確認します。
それが何をもたらすかについては、System.Collections.IDictionary の定義を参照してください。
if (listBox.ItemsSource is IDictionary)
{
DictionaryEntry pair = (DictionaryEntry)listBox.SelectedItem;
object value = pair.Value;
}
編集:KeyValuePair が DictionaryEntry にキャストできないことに気づいたときの代替手段
if (listBox.DataSource is IDictionary)
{
listBox.ValueMember = "Value";
object value = listBox.SelectedValue;
listBox.ValueMember = ""; //If you need it to generally be empty.
}
このソリューションではリフレクションを使用しますが、この場合は面倒な作業を行う必要はなく、ListBox が代わりにそれを行ってくれます。また、通常、データ ソースとして辞書を使用している場合は、常に ValueMember をリセットすることを回避できる可能性があります。
この質問が何年も前に行われたことは知っていますが、今でも公に公開されています。
このトピックとこのトピックでは、いくつかの例が提案されています。
タイプが辞書であるかどうかを判断します [重複]
しかし、不一致がほとんどないので、解決策を共有したいと思います
短い答え:
var dictionaryInterfaces = new[]
{
typeof(IDictionary<,>),
typeof(IDictionary),
typeof(IReadOnlyDictionary<,>),
};
var dictionaries = collectionOfAnyTypeObjects
.Where(d => d.GetType().GetInterfaces()
.Any(t=> dictionaryInterfaces
.Any(i=> i == t || t.IsGenericType && i == t.GetGenericTypeDefinition())))
より長い答え:
人が間違いを犯す理由は次のとおりだと私は考えています。
//notice the difference between IDictionary (interface) and Dictionary (class)
typeof(IDictionary<,>).IsAssignableFrom(typeof(IDictionary<,>)) // true
typeof(IDictionary<int, int>).IsAssignableFrom(typeof(IDictionary<int, int>)); // true
typeof(IDictionary<int, int>).IsAssignableFrom(typeof(Dictionary<int, int>)); // true
typeof(IDictionary<,>).IsAssignableFrom(typeof(Dictionary<,>)); // false!! in contrast with above line this is little bit unintuitive
これらのタイプがあるとしましょう:
public class CustomReadOnlyDictionary : IReadOnlyDictionary<string, MyClass>
public class CustomGenericDictionary : IDictionary<string, MyClass>
public class CustomDictionary : IDictionary
そしてこれらのインスタンス:
var dictionaries = new object[]
{
new Dictionary<string, MyClass>(),
new ReadOnlyDictionary<string, MyClass>(new Dictionary<string, MyClass>()),
new CustomReadOnlyDictionary(),
new CustomDictionary(),
new CustomGenericDictionary()
};
したがって、 .IsAssignableFrom() メソッドを使用する場合:
var dictionaries2 = dictionaries.Where(d =>
{
var type = d.GetType();
return type.IsGenericType && typeof(IDictionary<,>).IsAssignableFrom(type.GetGenericTypeDefinition());
}); // count == 0!!
インスタンスは取得できません
したがって、最良の方法は、すべてのインターフェイスを取得し、それらのいずれかが辞書インターフェイスであるかどうかを確認することです。
var dictionaryInterfaces = new[]
{
typeof(IDictionary<,>),
typeof(IDictionary),
typeof(IReadOnlyDictionary<,>),
};
var dictionaries2 = dictionaries
.Where(d => d.GetType().GetInterfaces()
.Any(t=> dictionaryInterfaces
.Any(i=> i == t || t.IsGenericType && i == t.GetGenericTypeDefinition()))) // count == 5
もう少し一般的に、代わりに実装されているかどうかを尋ねることもできます IDictionary
. 。その後、KeyValue コレクションはプレーンのままになります。 Objects
.
警告が出ていると思います。
オブジェクトが何か「である」かどうかをテストするとき、型システム (の一部) を再実装することになります。最初の「is a」の後にすぐに 2 番目の「is a」が続くことがよくあり、すぐにコードが型チェックでいっぱいになります。これは、少なくともオブジェクト指向設計では、型システムによって適切に処理されるはずです。
もちろん、質問の内容については何も知りません。私は、50 の異なるオブジェクトから文字列への変換を処理する、独自のコードベースの 2000 行のファイルを知っています...:(
if(typeof(IDictionary).IsAssignableFrom(listBox.ItemsSource.GetType()))
{
}