HappyNomad
HappyNomad

Reputation: 4548

typeof( ICollection<> ).GetTypeInfo().IsAssignableFrom( typeof(IList<>) )

I am trying to check the following

typeof( ICollection<> ).GetTypeInfo().IsAssignableFrom( targetProperty.PropertyType.GetTypeInfo() )

where the argument passed into IsAssignableFrom is an IList<Something>. But it is returning false.

The following also returns false.

typeof( ICollection<> ).GetTypeInfo().IsAssignableFrom( targetProperty.PropertyType.GetTypeInfo().GetGenericTypeDefinition() )

Even the following is returning false.

typeof( ICollection<> ).GetTypeInfo().IsAssignableFrom( typeof(IList<>) )

Shouldn't the latter definitely return true?

How can I get the correct result when targetProperty.PropertyType can be any type at all? It could be a List<T>, an ObservableCollection<T>, a ReadOnlyCollection<T>, a custom collection type, etc.

Upvotes: 2

Views: 3667

Answers (2)

Mike Zboray
Mike Zboray

Reputation: 40818

You have two open generic types. IsAssignableFrom interprets these like asking whether ICollection<T1> is assignable from IList<T2>. This is, in general, false. It is only true when T1 = T2. You need to do something to close the generic types with the same type argument. You could fill in the type as object or you could get the generic parameter type and use that:

var genericT = typeof(ICollection<>).GetGenericArguments()[0]; // a generic type parameter, T.
bool result = typeof(ICollection<>).MakeGenericType(genericT).IsAssignableFrom(typeof(IList<>).MakeGenericType(genericT)); // willl be true.

It seems GetGenericArguments is not available in PCL, and its behavior is different than GenericTypeArguments property. In a PCL you need to use GenericTypeParameters:

var genericT = typeof(ICollection<>).GetTypeInfo().GenericTypeParameters[0]; // a generic type parameter, T.
bool result = typeof(ICollection<>).MakeGenericType(genericT).GetTypeInfo().IsAssignableFrom(typeof(IList<>).MakeGenericType(genericT).GetTypeInfo()); // willl be true.

Upvotes: 5

Douglas
Douglas

Reputation: 54887

ICollection<T1> cannot be assigned from IList<T2> in general; otherwise, you could end up with situations where you assign, say, a List<char> to an ICollection<bool>.

typeof(ICollection<>).IsAssignableFrom(typeof(IList<>))          // false
typeof(ICollection<bool>).IsAssignableFrom(typeof(List<int>))    // false

You can, however, assign ICollection<T> from IList<T>, provided that the type parameter T is the same.

typeof(ICollection<bool>).IsAssignableFrom(typeof(List<bool>))   // true

Starting from C# 4, this also works for type covariance:

typeof(IEnumerable<BaseClass>).IsAssignableFrom(typeof(List<DerivedClass>)));         
    // true in C# 4
    // false in prior verions

Similarly, you can assign non-generic base interfaces from any generic type that implements them:

typeof(ICollection).IsAssignableFrom(typeof(List<bool>))         // true

Upvotes: 1

Related Questions