So I have class which accepts a generic type parameter and does a little special handling if the type parameter is a subclass of a given type.
IEnumerable<T> models = ...
// Special handling of MySpecialModel
if (filterString != null && typeof(MySpecialModel).IsAssignableFrom(typeof(T)))
{
var filters = filterString.Split(...);
models =
from m in models.Cast<MySpecialModel>()
where (from t in m.Tags
from f in filters
where t.IndexOf(f, StringComparison.CurrentCultureIgnoreCase) >= 0
select t)
.Any()
select (T)m;
}
But I'm getting an exception on the last line
Cannot convert type 'MySpecialModel' to 'T'
If I change the code to use as
instead of casting, I get this error.
The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint.
What am I missing here?
Update
This class needs can take any type parameter, including struct
s and built-in types, so a generic constraint would not be a suitable solution in my case.
Do Select(x => (MySpecialModel)x)
The LINQ Cast<T>
method will only work for casting elements to that the element already is (such as a base type, derived type, or interface). It is not intended to cast objects that are able to be cast to a target type. (e.g. new List<int>{1,2,3}.Cast<long>()
will throw an exception as well.
The above answer wasn't wrong, but it doesn't address the question.
Just because you have proved with reflection that a generic parameter is bound to a given type, doesn't mean that the compiler knows that it is. In order to make this work, you will need to cast your T
instance to a common type (e.g. object
), then cast it to the specific type. e.g. (changing the last line in your query to select (T)(object)m
should do the trick.
Try the following
select (T)(object)m;
At runtime you've verified that T
is a subtype of MySpecialModel
but the compiler doesn't have access to this information at compile time. It just sees an attempted conversion between 2 unrelated types: T
and MySpecialModel
.
To work around this you need to use object
as a middle man. The compiler understands how to convert MySpecialModel
to object
and to go from object
to T
.
The most straightforward fix is to cast to object
first before the cast to T
:
select (T)(object)m;
The problem is your check occurs at runtime, but the compiler doesn't know that T
must be an instance of MySpecialModel
within the if
statement. Therefore it just sees you are trying to cast to some arbitrary type T
from MySpecialModel
which is not safe, hence the error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With