public class Address
{
public string ZipCode {get; set;}
}
public class Customer
{
public Address Address {get; set;}
}
リフレクションを使用して「ZipCode」または「Address.ZipCode」にアクセスするにはどうすればよいですか。例えば:
Typeof(Customer).GetProperty("ZipCode")?
public class Address
{
public string ZipCode {get; set;}
}
public class Customer
{
public Address Address {get; set;}
}
リフレクションを使用して「ZipCode」または「Address.ZipCode」にアクセスするにはどうすればよいですか。例えば:
Typeof(Customer).GetProperty("ZipCode")?
次のようなものが必要になります。
PropertyInfo addressProperty = typeof(Customer).GetProperty("Address");
ProportyInfo zipCodeProperty = addressProperty.PropertyType.GetProperty("ZipCode");
object address = addressProperty.GetValue(customer, null);
object zipCode = zipCodeProperty.GetValue(address, null);
基本的に、文字列「Address.ZipCode」を取得して下に移動する場合は、「。」で分割する必要があります。次に、すべてのステップで適切なタイプのGetPropertyを呼び出してプロパティ自体を取得し、次にPropertyInfo.GetValueを呼び出してチェーン内の次の値を取得します。このようなもの:
public static object FollowPropertyPath(object value, string path)
{
Type currentType = value.GetType();
foreach (string propertyName in path.Split('.'))
{
PropertyInfo property = currentType.GetProperty(propertyName);
value = property.GetValue(value, null);
currentType = property.PropertyType;
}
return value;
}
このように呼んでください:
object zipCode = FollowPropertyPath(customer, "Address.ZipCode");
これは、プロパティのコンパイル時タイプで機能することに注意してください。実行時のタイプに対応する場合(たとえば、customer.AddressにZipCodeプロパティがなかったが、Addressによって返される実際のタイプにはあった場合)、に変更property.PropertyType
しproperty.GetType()
ます。
また、これにはエラー処理などがないことに注意してください:)
Jon Skeet の答えは問題ありませんが、プロパティ パス内の派生インスタンスを説明するために、彼のメソッドを少し拡張する必要がありました。
public static class ReflectorUtil
{
public static object FollowPropertyPath(object value, string path)
{
if (value == null) throw new ArgumentNullException("value");
if (path == null) throw new ArgumentNullException("path");
Type currentType = value.GetType();
object obj = value;
foreach (string propertyName in path.Split('.'))
{
if (currentType != null)
{
PropertyInfo property = null;
int brackStart = propertyName.IndexOf("[");
int brackEnd = propertyName.IndexOf("]");
property = currentType.GetProperty(brackStart > 0 ? propertyName.Substring(0, brackStart) : propertyName);
obj = property.GetValue(obj, null);
if (brackStart > 0)
{
string index = propertyName.Substring(brackStart + 1, brackEnd - brackStart - 1);
foreach (Type iType in obj.GetType().GetInterfaces())
{
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
obj = typeof(ReflectorUtil).GetMethod("GetDictionaryElement")
.MakeGenericMethod(iType.GetGenericArguments())
.Invoke(null, new object[] { obj, index });
break;
}
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IList<>))
{
obj = typeof(ReflectorUtil).GetMethod("GetListElement")
.MakeGenericMethod(iType.GetGenericArguments())
.Invoke(null, new object[] { obj, index });
break;
}
}
}
currentType = obj != null ? obj.GetType() : null; //property.PropertyType;
}
else return null;
}
return obj;
}
public static TValue GetDictionaryElement<TKey, TValue>(IDictionary<TKey, TValue> dict, object index)
{
TKey key = (TKey)Convert.ChangeType(index, typeof(TKey), null);
return dict[key];
}
public static T GetListElement<T>(IList<T> list, object index)
{
return list[Convert.ToInt32(index)];
}
}
property.PropertyType を使用すると、obj クラスで定義されたプロパティ タイプが取得され、obj.GetType() を使用すると、プロパティのインスタンスの実際のタイプが取得されます。
編集:@Oliver - あなたは絶対に正しいです、それを指摘してくれてありがとう. 一般的なリストと辞書を使用できるようにメソッドを調整しました。解析部分は好きではありませんが、このスレッドで Marc Gravell の巧妙なアイデアを使用して、インデクサー プロパティの値を取得しました。
既存の答えは問題ありません。単なる代替の観点:多くのシナリオでは、直接リフレクションではなくSystem.ComponentModelを使用することが望ましいです。これにより、実行時のプロパティシナリオ、つまり、DataTableのDataViewが列をプロパティとして公開する方法が可能になります。
パフォーマンスに関して-デフォルトではこれはほとんど同じですが、これを大量に実行している場合(たとえば、バルクデータのインポート/エクスポート)、 HyperDescriptorのおかげで、このアプローチを使用して実際にパフォーマンスを大幅に向上させることができます。
System.ComponentModelを使用するためのコードは似ていますが、微妙に異なります。
static void Main()
{
object obj = new Customer { Address = new Address { ZipCode = "abcdef" } };
object address = GetValue(obj, "Address");
object zip = GetValue(address, "ZipCode");
Console.WriteLine(zip);
}
static object GetValue(object component, string propertyName)
{
return TypeDescriptor.GetProperties(component)[propertyName].GetValue(component);
}
これにより、データバインディングを使用して「Address.ZipCode」にバインドした場合と同じ処理が可能になります(リストなどの詳細を確認できます)。
(予想されるタイプであることがわかっている場合は、zipを文字列などとしてキャストできることに注意してください)
ディープパス(データバインディングが使用するのと同じリスト処理を含む)から値を取得するには、次のようなものを使用します。
static object ResolveValue(object component, string path) {
foreach(string segment in path.Split('.')) {
if (component == null) return null;
if(component is IListSource) {
component = ((IListSource)component).GetList();
}
if (component is IList) {
component = ((IList)component)[0];
}
component = GetValue(component, segment);
}
return component;
}
リストのものは、通常のデータバインディングの動作を大まかに反映しています(ただし、バインディングコンテキスト、通貨マネージャーなどのいくつかのものは省略されています)
typeof (Customer).GetProperty("Address").PropertyType.GetProperty("ZipCode")
アダビロン、
タイプのみを取得する必要がある場合、および実際のオブジェクト インスタンスがない場合のために、コードのバージョンを作成しました。
public static Type FollowPropertyPath<T>(string path)
{
if (path == null) throw new ArgumentNullException("path");
Type currentType = typeof(T);
foreach (string propertyName in path.Split('.'))
{
int brackStart = propertyName.IndexOf("[");
var property = currentType.GetProperty(brackStart > 0 ? propertyName.Substring(0, brackStart) : propertyName);
if (property == null)
return null;
currentType = property.PropertyType;
if (brackStart > 0)
{
foreach (Type iType in currentType.GetInterfaces())
{
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof (IDictionary<,>))
{
currentType = iType.GetGenericArguments()[1];
break;
}
if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof (ICollection<>))
{
currentType = iType.GetGenericArguments()[0];
break;
}
}
}
}
return currentType;
}