简体   繁体   English

迭代索引属性(反射)

[英]iterating through an indexed property (Reflection)

I want to itterate over an indexed property that I only have access to via reflection, 我想对我只能通过反射访问的索引属性进行迭代,

but ( and I say this in the full knowledge that there's probably an embarrassingly simple answer, MSDN/Google fail =/ ) I cannot find/think of a way besides incrementing a counter over the PropertyInfo.GetValue(prop, counter) until the TargetInvocationException is thrown. 但是(并且我完全知道这可能是一个令人尴尬的简单答案,MSDN / Google失败= /)除了在PropertyInfo.GetValue(prop, counter)递增计数器之外我找不到/想到一种方法直到TargetInvocationException被扔了。

ala: 翼:

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

now, there are some issues with this... very ugly.. solution.. 现在,这有一些问题...非常难看..解决方案..

what if it's multi-dimensional or not indexed by integers for example... 如果它是多维的或者没有被整数索引,例如...

heres the test code I'm using to try and get it working if anyone needs it. 下面是我正在使用的测试代码,如果有人需要它,可以让它运行起来。 If anyone's interested I'm making a custom caching system and .Equals doesn't cut it. 如果有人有兴趣我正在制作一个自定义缓存系统而且.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() );
        }
    }

The getter of an indexer is just like normal method, except it takes square brackets, not round ones. 索引器的getter就像普通方法一样,除了它采用方括号而不是圆括号。 You wouldn't expect to be able to automatically determine the range of acceptable values for a method, so it's not feasible for an indexer. 您不希望能够自动确定方法的可接受值的范围,因此对于索引器来说这是不可行的。

Indexers will be compiled to methods. 索引器将编译为方法。 Here is an example: 这是一个例子:

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

It will be compiled to something like this: 它将编译成这样的东西:

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

The following code can't be compiled because there are two double get_Item(int) methods in the class. 无法编译以下代码,因为类中有两个double get_Item(int)方法。 Indexer is a magic of the compiler. Indexer是编译器的神奇之处。

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

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

Having sequential index numbers in an indexed property is nothing you can bet on. 在索引属性中具有顺序索引号是没有什么可以打赌的。
Indexed properties are not arrays. 索引属性不是数组。
Counter example: 反例:

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

Depending on the type you usually have other methods to get the possible index values, in this case dictionary.Keys . 根据类型,您通常有其他方法来获取可能的索引值,在本例中为dictionary.Keys If it possible with your types I would try in this order 如果你的类型可以,我会尝试这个顺序

  1. Implement IEnumerable<T> for the type itself. 为类型本身实现IEnumerable<T>
  2. If you have several indexed properties you can implement a corresponding IEnumerable<T> property for each indexed property. 如果您有多个索引属性,则可以为每个索引属性实现相应的IEnumerable<T>属性。

If you have no specification of valid values and no method of asking what the valid values are, then you are pretty much out of luck. 如果你没有有效值的规范,也没有询问有效值是什么的方法,那么你几乎没有运气。

You can use PropertyInfo.GetIndexParameters to find out the number and type of indexed property parameters. 您可以使用PropertyInfo.GetIndexParameters来查找索引属性参数的数量和类型。

I don't think there is anything you can do about finding what "legal" values for these parameters are, unless you "cheat" and use inside information you may have on what that property is. 我不认为你可以做些什么来找到这些参数的“合法”值,除非你“欺骗”并使用你可能拥有的内部信息。

Managed to make an improvement, mind, also found out that this test code suffers from infinite loops for self references (Array.Syncroot for example) 管理进行改进,介意,还发现这个测试代码遭受自我引用的无限循环(例如Array.Syncroot)

In a nutshell it now finds things that inherit from IEnumerable (which is most indexed things) and use a foreach loop on those and coupled with the knowledge that the existing (ugly) code works for strings, it's now allot more thorough then it used to be... 简而言之,它现在发现从IEnumerable继承的东西(这是大多数索引的东西)并在那些上使用foreach循环,再加上现有(丑陋)代码适用于字符串的知识,它现在分配得比以前更彻底是...

glad but disapointed that there doesn't seem to be a nice answer. 很高兴但却感到沮丧,似乎没有一个好的答案。

Thanks for the help everyone 感谢大家的帮助


Updated test code if someone finds themselves in a similar position 如果有人发现自己处于类似位置,则更新测试代码

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

The above codes and the related ones to this question were really helpful for the problem that I was facing. 以上代码和这个问题的相关内容对我所面临的问题非常有帮助。 I'm posting my code and I hope this one works for you guys too. 我发布了我的代码,我希望这个代码也适用于你们。

public ActionResult Survey(SurveyCollection surveyCollection) { if (surveyCollection != null) { Answer_DropDownCordinateOptionList traceObject = new Answer_DropDownCordinateOptionList(); IList traceObjectCollection = new List(); traceObjectCollection = ExtractNestedObjects(surveyCollection, traceObject, traceObjectCollection); }

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM