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
- [Flags]
- public enum ErrorState
- {
- Ok = 1,
- ConnectionError = 2,
- HoustonWeHaveAProblem = 4
- // etc...
- }
- public abstract class MyBase
- {
- private ErrorState _errorState;
- public ErrorState ErrorState
- {
- get { return _errorState; }
- set { _errorState = value; }
- }
- public ErrorState ConsolidatedState
- {
- get
- {
- ErrorState state = this.ErrorState;
- PropertyInfo[] properties = this.GetType().GetProperties();
- foreach( PropertyInfo property in properties )
- {
- // We don't want to inspect the ConsolidatedState property
- // (this property) else we get stuck in a loop, causing a
- // StackOverflowException.
- if( property.Name == "ConsolidatedState" )
- {
- continue;
- }
- // Is this property an array?
- if( property.PropertyType.IsArray )
- {
- // Is this property an array of objects derived from
- // MyBase?
- MyBase[] componentArray
- = property.GetValue( this, null ) as MyBase[];
- if( componentArray != null )
- {
- // Yes, so inspect their ConsolidatedState properties.
- foreach( MyBase c in componentArray )
- {
- state |= c.ConsolidatedState;
- }
- }
- continue;
- }
- // Is this property an indexer?
- if( property.GetIndexParameters().Length > 0 )
- {
- // it's probably an indexer, so ignore it
- continue;
- }
- // Is this property of a type derived from MyBase?
- if( property.PropertyType.IsSubclassOf( typeof( MyBase ) ) )
- {
- // Yes, so it also has a ConsolidatedState property
- MyBase component
- = property.GetValue( this, null ) as MyBase;
- if( component != null )
- {
- state |= component.ConsolidatedState;
- }
- continue;
- }
- // Is this property a generic type?
- if( property.PropertyType.IsGenericType )
- {
- // Is this property a generic collection of a type
- // derived from MyBase?
- //Collection<MyBase> componentCollection
- // = property.GetValue( this, null )
- // as Collection<MyBase>; // returns null
- // Is this property a generic collection of MyDerived1?
- IEnumerable<MyDerived1> componentCollection
- = property.GetValue( this, null )
- as IEnumerable<MyDerived1>;
- if( componentCollection != null )
- {
- // Yes, so inspect their ConsolidatedState properties.
- foreach( MyBase c in componentCollection )
- {
- state |= c.ConsolidatedState;
- }
- }
- continue;
- }
- }
- return state;
- }
- }
- }
- public class MyDerived1 : MyBase
- {
- // ...
- }
- public class MyDerived2 : MyBase
- {
- public MyDerived1 Property1
- {
- get { return new MyDerived1(); }
- }
- public MyDerived1[] Property2
- {
- get { return new MyDerived1[4]; }
- }
- public Collection<MyDerived1> Property3
- {
- get { return new Collection<MyDerived1>(); }
- }
- }
// 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.