Reputation: 4768
I'm trying to fetch all ICollection<T>
properties from class of unknown type. Also, type T (what the collection is of) is not known at compile time. Firstly I've tried this approach:
foreach (var property in entity.GetType().GetProperties())
{
if (typeof(ICollection).IsAssignableFrom(property.PropertyType) || typeof(ICollection<>).IsAssignableFrom(property.PropertyType))
{
// do something
}
}
but it's not working (evaluating false even for ICollection
properties).
I got it working like this:
foreach (var property in entity.GetType().GetProperties())
{
var getMethod = property.GetGetMethod();
var test = getMethod.Invoke(entity, null);
if (test is ICollection)
{
// do something
}
}
but I do not want to execute all getters. Why is the first piece of code not working? How can I find ICollection
properties without executing all getters?
Upvotes: 4
Views: 3184
Reputation: 2831
After attempting to use the accepted answer I had a situation where only partial matches where being returned. My object had 3 ICollection<T>
properties and I was only being returned with 2. I spent some time testing and trying to figure out why, but I moved on and wrote this:
public static IEnumerable<PropertyInfo> GetICollectionProperties(object entity)
{
return entity.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType
&& p.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>));
}
I have tested with the same test cases and I am getting the correct results returned from this method.
This will not pick up non-generic ICollections, but the OP did ask for ICollection<T>
properties, all-though it could be easily re-factored to include. It will also not return properties that are not exactly of type ICollection (ie, Eugene's List and IDictionary in his test case would not be returned (but again, what the OP wanted)).
Upvotes: 2
Reputation: 10401
It turns out that with IsAssignableFrom check you can't find whether the interface is a derivative of another interface:
Console.WriteLine(typeof(ICollection<>).IsAssignableFrom(typeof(ICollection<Int32>)));
Console.WriteLine(typeof(ICollection<Int32>).IsAssignableFrom(typeof(ICollection<>)));
will both write false;
With little help from here this is the best solution I can come of:
static IEnumerable<PropertyInfo> GetICollectionOrICollectionOfTProperties(this Type type)
{
// Get properties with PropertyType declared as interface
var interfaceProps =
from prop in type.GetProperties()
from interfaceType in prop.PropertyType.GetInterfaces()
where interfaceType.IsGenericType
let baseInterface = interfaceType.GetGenericTypeDefinition()
where (baseInterface == typeof(ICollection<>)) || (baseInterface == typeof(ICollection))
select prop;
// Get properties with PropertyType declared(probably) as solid types.
var nonInterfaceProps =
from prop in type.GetProperties()
where typeof(ICollection).IsAssignableFrom(prop.PropertyType) || typeof(ICollection<>).IsAssignableFrom(prop.PropertyType)
select prop;
// Combine both queries into one resulting
return interfaceProps.Union(nonInterfaceProps);
}
This solution may yield some duplicates(it is hardly possible, but to be sure use Distinct
) and it doesn't look very nice.
But it works well on such class with properties with both the interface return types and concrete return types :
class Collections
{
public List<Int32> ListTProp
{
get;
set;
}
public IDictionary<Int32, String> IDictionaryProp
{
get;
set;
}
public ICollection ICollectionProp
{
get;
set;
}
public ICollection<DateTime> IDateTimeCollectionProp
{
get;
set;
}
}
Upvotes: 5