перебираем индексированное свойство (Reflection) - PullRequest
10 голосов
/ 24 ноября 2010

Я хочу перебрать индексированное свойство, к которому у меня есть доступ только через отражение,

но (и я говорю это из-за полного понимания того, что, вероятно, есть неловко простой ответ, MSDN / Google fail = /), я не могу найти / придумать способ кроме увеличения счетчика над PropertyInfo.GetValue(prop, counter) до TargetInvocationException брошен.

ала:

foreach ( PropertyInfo prop in obj.GetType().GetProperties() )
{
    if ( prop.GetIndexParameters().Length > 0 )
    {
        // get an integer count value, by incrementing a counter until the exception is thrown
        int count = 0;
        while ( true )
        {
            try
            {
                prop.GetValue( obj, new object[] { count } );
                count++;
            }
            catch ( TargetInvocationException ) { break; }
        }

        for ( int i = 0; i < count; i++ )
        {
            // process the items value
            process( prop.GetValue( obj, new object[] { i } ) );
        }
    }
}

Теперь есть некоторые проблемы с этим ... очень уродливым .. решением ..

что если он многомерный или не проиндексирован, например, целыми числами ...

вот тестовый код, который я использую, чтобы попытаться заставить его работать, если кому-то это нужно. Если кому-то интересно, я делаю собственную систему кеширования, а .Equals не сокращает ее.

    static void Main()
    {
        object str = new String( ( "Hello, World" ).ToArray() );

        process( str );

        Console.ReadKey();
    }

    static void process( object obj )
    {
        Type type = obj.GetType();

        PropertyInfo[] properties = type.GetProperties();

        // if this obj has sub properties, apply this process to those rather than this.
        if ( properties.Length > 0 )
        {
            foreach ( PropertyInfo prop in properties )
            {
                // if it's an indexed type, run for each
                if ( prop.GetIndexParameters().Length > 0 )
                {
                    // get an integer count value
                    // issues, what if it's not an integer index (Dictionary?), what if it's multi-dimensional?
                    // just need to be able to iterate through each value in the indexed property
                    int count = 0;
                    while ( true )
                    {
                        try
                        {
                            prop.GetValue( obj, new object[] { count } );
                            count++;
                        }
                        catch ( TargetInvocationException ) { break; }
                    }

                    for ( int i = 0; i < count; i++ )
                    {
                        process( prop.GetValue( obj, new object[] { i } ) );
                    }
                }
                else
                {
                    // is normal type so.
                    process( prop.GetValue( obj, null ) );
                }
            }
        }
        else
        {
            // process to be applied to each property
            Console.WriteLine( "Property Value: {0}", obj.ToString() );
        }
    }

Ответы [ 6 ]

8 голосов
/ 24 ноября 2010

Метод получения индексатора аналогичен обычному методу, за исключением того, что он принимает квадратные скобки, а не круглые. Вы не ожидаете, что сможете автоматически определять диапазон допустимых значений для метода, поэтому для индексатора это невозможно.

4 голосов
/ 24 ноября 2010

Индексаторы будут компилироваться в методы.Вот пример:

class IndexedData
{ 
    public double this[int index]
    {
        get { return (double)index; }
    }
}

Он будет скомпилирован примерно так:

public double get_Item(int index)
{
    return (double)index;
}

Следующий код не может быть скомпилирован, поскольку в методе есть два double get_Item(int) метода.учебный класс.Индексатор - это магия компилятора.

class IndexedData
{ 
    public double this[int index]
    {
        get { return (double)index; }
    }

    public double get_Item(int index)
    {
        return 1d;
    }
}
3 голосов
/ 24 ноября 2010

С последовательными индексными номерами в индексированном свойстве нельзя делать ставки.
Индексированные свойства не являются массивами.
Пример счетчика:

Dictionary<int, bool> dictionary = new Dictionary<int, bool>();
dictionary[1] = true;
dictionary[5] = false;

В зависимости от типа, который вы обычно используетеметоды для получения возможных значений индекса, в данном случае dictionary.Keys.Если это возможно с вашими типами, я бы попробовал в этом порядке

  1. Реализовать IEnumerable<T> для самого типа.
  2. Если у вас есть несколько индексированных свойств, вы можетереализуйте соответствующее свойство IEnumerable<T> для каждого индексированного свойства.

Если у вас нет спецификации действительных значений и нет способа узнать, что это за действительные значения, то вам почти не повезло.

2 голосов
/ 24 ноября 2010

Удалось улучшить ситуацию, но также обнаружил, что этот тестовый код страдает бесконечными циклами для собственных ссылок (например, Array.Syncroot)

В двух словах, теперь он находит вещи, которые наследуются от IEnumerable (который является большинством индексированных вещей), и использует цикл foreach для них и в сочетании со знанием того, что существующий (уродливый) код работает для строк, теперь он выделен более тщательно Раньше было ...

рад, но разочарован тем, что нет хорошего ответа.

Спасибо за помощь всем


Обновлен тестовый код, если кто-то оказался в аналогичной позиции

    static void process( object obj )
    {
        Type type = obj.GetType();

        PropertyInfo[] properties = type.GetProperties();

        // if this obj has sub properties, apply this process to those rather than this.
        if ( properties.Length > 0 )
        {
            foreach ( PropertyInfo prop in obj.GetType().GetProperties() )
            {
                    if ( prop.PropertyType.FindInterfaces( ( t, c ) => t == typeof( IEnumerable ), null ).Length > 0 )
                    {
                        MethodInfo accessor = prop.GetGetMethod();
                        MethodInfo[] accessors = prop.GetAccessors();

                        foreach ( object item in (IEnumerable)obj )
                        {
                            process( item );
                        }
                    }
                    else if ( prop.GetIndexParameters().Length > 0 )
                    {
                        // get an integer count value, by incrementing a counter until the exception is thrown
                        int count = 0;
                        while ( true )
                    {
                        try
                        {
                            prop.GetValue( obj, new object[] { count } );
                            count++;
                        }
                        catch ( TargetInvocationException ) { break; }
                    }

                    for ( int i = 0; i < count; i++ )
                    {
                        // process the items value
                        process( prop.GetValue( obj, new object[] { i } ) );
                    }
                }
                else
                {
                    // is normal type so.
                    process( prop.GetValue( obj, null ) );
                }
            }
        }
        else
        {
            // process to be applied to each property
            Console.WriteLine( "Property Value: {0}", obj.ToString() );
        }
    }
2 голосов
/ 24 ноября 2010

Вы можете использовать PropertyInfo.GetIndexParameters, чтобы узнать количество и тип индексированных параметров свойств.

Я не думаю, что вы можете что-то сделать, чтобы найти то, что "законно"Значения этих параметров равны, если только вы не «обманываете» и не используете внутреннюю информацию, которая может у вас быть о том, что это свойство.

0 голосов
/ 28 мая 2014

Приведенные выше коды и связанные с этим вопросом действительно помогли решить проблему, с которой я столкнулся.Я публикую свой код, и я надеюсь, что этот код тоже работает для вас.

public ActionResult Survey(SurveyCollection surveyCollection) { if (surveyCollection != null) { Answer_DropDownCordinateOptionList traceObject = new Answer_DropDownCordinateOptionList(); IList traceObjectCollection = new List(); traceObjectCollection = ExtractNestedObjects(surveyCollection, traceObject, traceObjectCollection); }</p> <pre><code> return View(surveyCollection); } private static IList<T> ExtractNestedObjects<T>(object baseObject, T findObject, IList<T> resultCollection) { if (baseObject != null && findObject != null) { Type typeDestination = findObject.GetType(); Type typeSource = baseObject.GetType(); PropertyInfo[] propertyInfoCollection = typeSource.GetProperties(); foreach (PropertyInfo propertyInfo in propertyInfoCollection) { if (propertyInfo.PropertyType.FindInterfaces((t, c) => t == typeof(IEnumerable), null).Length > 0) { if(propertyInfo.GetValue(baseObject, null) != null) { if(propertyInfo.GetValue(baseObject, null).GetType().IsPrimitive) { ExtractNestedObjects<T>(propertyInfo.GetValue(baseObject, null), findObject, resultCollection); } else if (propertyInfo.GetValue(baseObject, null).GetType().IsGenericType) { foreach (var item in (IList)propertyInfo.GetValue(baseObject, null)) { ExtractNestedObjects<T>(item, findObject, resultCollection); } } } } else { if (propertyInfo.Name == typeDestination.Name) { if (propertyInfo.GetValue(baseObject, null) != null) { resultCollection.Add((T)propertyInfo.GetValue(baseObject, null)); } } ExtractNestedObjects<T>(propertyInfo.GetValue(baseObject, null), findObject, resultCollection); } } } return resultCollection; }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...