By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,028 Members | 1,092 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 432,028 IT Pros & Developers. It's quick & easy.

Reflecting generic collections of derived types

P: 5
I am trying to use reflection in a property of a base type to inspect the properties of an instance of a type which is derived from that base type, when the properties can themselves be instances of types derived from that base type, or arrays or generic collections of instances of types derived from that base type.

All is well until I come to the properties which are generic collections, I don't seem to be able to find an elegant way of inspecting these.

Here is some code to illustrate what I'm trying to do. The base type, and therefore the derived types, have an ErrorState property, this is an enumeration of things which could be wrong with instances of those types. There is also a ConsolidatedState property, this is the one which uses reflection to return a combination of all the things which may be wrong with the current instance and all of its properties.

Expand|Select|Wrap|Line Numbers
  1.  
  2. [Flags]
  3. public enum ErrorState
  4. {
  5.     Ok = 1,
  6.     ConnectionError = 2,
  7.     HoustonWeHaveAProblem = 4
  8.     // etc...
  9. }
  10.  
  11. public abstract class MyBase
  12. {
  13.     private ErrorState _errorState;
  14.  
  15.     public ErrorState ErrorState
  16.     {
  17.         get { return _errorState; }
  18.         set { _errorState = value; }
  19.     }
  20.  
  21.     public ErrorState ConsolidatedState
  22.     {
  23.         get
  24.         {
  25.             ErrorState state = this.ErrorState;
  26.             PropertyInfo[] properties = this.GetType().GetProperties();
  27.             foreach( PropertyInfo property in properties )
  28.             {
  29.                 // We don't want to inspect the ConsolidatedState property
  30.                 // (this property) else we get stuck in a loop, causing a
  31.                 // StackOverflowException.
  32.                 if( property.Name == "ConsolidatedState" )
  33.                 {
  34.                     continue;
  35.                 }
  36.  
  37.                 // Is this property an array?
  38.                 if( property.PropertyType.IsArray )
  39.                 {
  40.                     // Is this property an array of objects derived from 
  41.                     // MyBase?
  42.                     MyBase[] componentArray 
  43.                         = property.GetValue( this, null ) as MyBase[];
  44.                     if( componentArray != null )
  45.                     {
  46.                         // Yes, so inspect their ConsolidatedState properties.
  47.                         foreach( MyBase c in componentArray )
  48.                         {
  49.                             state |= c.ConsolidatedState;
  50.                         }
  51.                     }
  52.                     continue;
  53.                 }
  54.  
  55.                 // Is this property an indexer?
  56.                 if( property.GetIndexParameters().Length > 0 )
  57.                 {
  58.                     // it's probably an indexer, so ignore it
  59.                     continue;
  60.                 }
  61.  
  62.                 // Is this property of a type derived from MyBase?
  63.                 if( property.PropertyType.IsSubclassOf( typeof( MyBase ) ) )
  64.                 {
  65.                     // Yes, so it also has a ConsolidatedState property
  66.                     MyBase component 
  67.                         = property.GetValue( this, null ) as MyBase;
  68.                     if( component != null )
  69.                     {
  70.                         state |= component.ConsolidatedState;
  71.                     }
  72.                     continue;
  73.                 }
  74.  
  75.                 // Is this property a generic type?
  76.                 if( property.PropertyType.IsGenericType )
  77.                 {
  78.                     // Is this property a generic collection of a type
  79.                     // derived from MyBase?
  80.                     //Collection<MyBase> componentCollection
  81.                     //    = property.GetValue( this, null ) 
  82.                     //    as Collection<MyBase>; // returns null
  83.  
  84.                     // Is this property a generic collection of MyDerived1?
  85.                     IEnumerable<MyDerived1> componentCollection 
  86.                         = property.GetValue( this, null )
  87.                         as IEnumerable<MyDerived1>;
  88.                     if( componentCollection != null )
  89.                     {
  90.                         // Yes, so inspect their ConsolidatedState properties.
  91.                         foreach( MyBase c in componentCollection )
  92.                         {
  93.                             state |= c.ConsolidatedState;
  94.                         }
  95.                     }
  96.                     continue;
  97.                 }
  98.  
  99.             }
  100.             return state;
  101.         }
  102.     }
  103. }
  104.  
  105. public class MyDerived1 : MyBase
  106. {
  107.     // ...
  108. }
  109.  
  110. public class MyDerived2 : MyBase
  111. {
  112.     public MyDerived1 Property1
  113.     {
  114.         get { return new MyDerived1(); }
  115.     }
  116.  
  117.     public MyDerived1[] Property2
  118.     {
  119.         get { return new MyDerived1[4]; }
  120.     }
  121.  
  122.     public Collection<MyDerived1> Property3
  123.     {
  124.         get { return new Collection<MyDerived1>(); }
  125.     }
  126. }
  127.  
  128.  
The problem is with this line, which I've commented out in the ConsolidatedState property of MyBase:

// Is this property a generic collection of a type
// derived from MyBase?
Collection<MyBase> componentCollection
= property.GetValue( this, null )
as Collection<MyBase>; // returns null

Having determined that the property being inspected is a generic collection, I want to know whether the items in the collection are of a type which is derived from MyBase and therefore has an ErrorState.

Having read Casting generic collections & inheritance in the archive forum, I understand now why the cast above doesn't work. As one of the contributors to that thread succinctly puts it, a List of (Farmers) is also a List of (Persons), but a (List of Farmers) is not a (List of Persons), and I'm trying to cast a (List of Farmers) to a (List of Persons).

What I want to do is attempt to cast something which I know is a generic collection, into any sort of array / list / collection in order that I can determine whether its items are derived from MyBase and therefore whether they have a ConsolidatedState property.

As you can see, I came up with a solution, but it's not very elegant because it depends on MyBase knowing that one or more of its derived types has a property which is a generic collection of instances of another specific type derived from MyBase. If someone later writes another type derived from MyBase, with a property which is a generic collection of instances of a type other than MyDerived1, then its ConsolidatedState property won't be able to inspect that collection unless MyBase is also changed to cater for properties of that type.

I feel sure that there must be a more elegant solution than this, one in which the author of MyBase doesn't need to know any of the implementation details of any of the types derived from MyBase. Can anyone suggest one?

Thank you and apologies for a long first post.

Simon.
Jul 18 '09 #1
Share this Question
Share on Google+
2 Replies


P: 5
Just check if the property is IEnumerable (non-generic version) and then cast objects to whatever you want. That way you will cover arrays too because arrays are IEnumerable.
Aug 5 '09 #2

P: 5
@icesign
Thank you!

I changed the lines within the ConsolidatedState property

Expand|Select|Wrap|Line Numbers
  1. // Is this property a generic collection of MyDerived1?
  2. IEnumerable<MyDerived1> componentCollection 
  3.     = property.GetValue( this, null )
  4.     as IEnumerable<MyDerived1>;
  5. if( componentCollection != null )
  6. {
  7.     // Yes, so inspect their ConsolidatedState properties.
  8.     foreach( MyBase c in componentCollection )
  9.     {
  10.         state |= c.ConsolidatedState;
  11.     }
  12. }
  13.  
to

Expand|Select|Wrap|Line Numbers
  1. // Is this property IEnumerable?
  2. IEnumerable objectCollection 
  3.     = property.GetValue( this, null ) as IEnumerable;
  4. if( objectCollection != null )
  5. {
  6.     // Yes, it's IEnumerable, so iterate through its items
  7.     foreach( object o in objectCollection )
  8.     {
  9.         // Is this item of a type derived from MyBase?
  10.         MyBase c = o as MyBase;
  11.         if( c != null )
  12.         {
  13.             // Yes, so get its ConsolidatedState property
  14.         state |= c.ConsolidatedState;
  15.         }
  16.     }
  17. }
  18.  
and it all seems to work fine now.

Thank you Icesign :-)
Aug 6 '09 #3

Post your reply

Sign in to post your reply or Sign up for a free account.