Лучший способ получить дополнительные свойства с помощью GetProperty



public class Address
{
public string ZipCode {get; set;}
}

public class Customer
{
public Address Address {get; set;}
}


Как я могу получить доступ к "ZipCode"или" Address.Индекс" с отражением? Например:



Typeof(Customer).GetProperty("ZipCode")?
627   6  

6 ответов:

Вам понадобится что-то вроде:

PropertyInfo addressProperty = typeof(Customer).GetProperty("Address");
ProportyInfo zipCodeProperty = addressProperty.PropertyType.GetProperty("ZipCode");

object address = addressProperty.GetValue(customer, null);
object zipCode = zipCodeProperty.GetValue(address, null);

В основном, если вы хотите взять строку " адрес.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");
Обратите внимание, что это работает с типами свойств во время компиляции. Если вы хотите, чтобы он справился с исполнением тип времени (например, если клиент.Address не имеет свойства ZipCode, но фактический тип, возвращаемый Address, имеет), затем измените property.PropertyType на property.GetType().

Также обратите внимание, что это не имеет никакой обработки ошибок и т.д.:)

Ответ Джона Скита прекрасен, хотя мне пришлось немного расширить его метод, чтобы учесть производные экземпляры в пути свойств:

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)];
    }

}

Использование свойства.PropertyType будет Вам тип недвижимости определена на объект класса, в то время как использование obj.Функция GetType () выдаст вам фактический тип экземпляра свойства.

EDIT: @Oliver-вы абсолютно правы, спасибо, что отметили это. Я скорректировал метод, чтобы учесть общие списки и словари. Хотя мне это не нравится. разбирая часть, я использовал умную идею Марка Гравелла в этой нити, чтобы получить значения свойства indexer.

Существующие ответы прекрасны; просто альтернативная перспектива: во многих сценариях желательно использовать систему.ComponentModel, а не прямое отражение, так как это позволяет создавать сценарии свойств среды выполнения - например, как DataView объекта DataTable предоставляет столбцы в виде свойств.

Performance wise-по умолчанию это в основном идентично, но если вы делаете много этого (например, массовый импорт/экспорт данных), вы действительно можете получить значительное увеличение производительности с помощью этого подход, любезно предоставленный Гипердескриптором .

Использовать систему.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);
}

Это дает вам такую же обработку, как если бы вы использовали привязку данных для привязки к "адресу.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;
    }

Проблема: Слабо Типизированные Переменные:

@ jonskeet's FollowPropertyPath(...) метод почти точно отвечал моим потребностям; за исключением того, что моя собственность была слабой типизацией; следовательно, currentType = property.PropertyType просто возвращена системы.Объект и сбой на следующей итерации цикла foreach.

Решение: Чтобы использовать тип времени выполнения, а не тип времени разработки, я настроил метод следующим образом:

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 = value.GetType();    // <-- Change
    }
    return value;
}

Comments

    Ничего не найдено.