I'm trying to create a list of ViewModels out of a DTO by calling a select on the list of DTO's. However, the compiler gives me an error saying:
The type arguments for method cannot be inferred from the usage try specifying the type arguments
My question is, why can't it? Both TextSectionDTO
and ImageSectionDTO
are derived from SectionDTO
. I'm trying to create a List
of Sections
, and both TextSection
and ImageSection
are derived from Section
.
I know this question is close to some other questions posted on here, but I wasn't able to find an answer there.
This is my code:
private List<Section> BuildSectionViewModel(IEnumerable<SectionDTO> ss )
{
var viewModels = ss.Select((SectionDTO s) =>
{
switch (s.SectionType)
{
case Enums.SectionTypes.OnlyText:
return new TextSection((TextSectionDTO) s);
case Enums.SectionTypes.OnlyImage:
return new ImageSection((ImageSectionDTO) s);
default:
throw new Exception("This section does not exist - FIXME");
}
}).ToList();
return viewModels;
}
When I change the types so that I only accept superclass SectionDTO and only return Section (I make them both normal classes in this scenario) the select works like you'd expect. Then when I change the types to only TextSectionDTO and TextSection (changing the abstracts back), the select doesn't work anymore.
I'd like a solution so that I can get this to work with the construction I have right now, though I'm more interested in why this does not work the way it is. Even if I can get this to work I'll probably refactor this later.
Note:
case Enums.SectionTypes.OnlyText:
return new TextSection((TextSectionDTO) s);
case Enums.SectionTypes.OnlyImage:
return new ImageSection((ImageSectionDTO) s);
These two cases return differen types. The compiler isn´t smart enough to check if those types derive from the same base-type so you have to cast them explicitely:
case Enums.SectionTypes.OnlyText:
return (SectionDTO) new TextSection((TextSectionDTO) s);
case Enums.SectionTypes.OnlyImage:
return (SectionDTO) new ImageSection((ImageSectionDTO) s);
Why this isn´t implemented on the compiler? I assume this is because the compiler has to check for many different types. Assume that your two types Foo1
and Foo2
do not directly derive from Bar
but from two different ones (Bar1
and Bar2
accordingly) that themselfs inherit from Bar
. Now the compiler should check if Foo1
and Foo2
can be assigned to any common base-class which they can not, and also check if they derive from something that HAS a common base-class (Bar
). In the end we had to check the whole inheritance-chain until object
, not mentioning any interfaces which should also be checked.
class Foo1 : Bar1 {}
class Foo2 : Bar2 {}
class Bar1 : Bar {}
class Bar2 : Bar {}
Select
method has two type aguments - TSource
and TResult
. Since you are invoking it on IEnumerable<SectionDTO>
, TSource
is inferred to be SectionDTO
, so ss.Select((SectionDTO s) =>
is not needed and can be just ss.Select(s => ...
.
The problem is the TResult
which in your case cannot be inferred. Why? You have two returns - TextSection
and ImageSection
. They are not the same and none of them is a base of the another. What do you think the compiler should infer? You think that the answer should be the common base type Section
, but the same could apply to a common base object
or any common base class/interface of the two type. In other words, the result is ambiguous, so instead of guessing what is your intention, the compiler requires you to specify that explicitly. Similar to ternary operator ? :
it would be sufficient to specify the common base in just one branch, so the following should resolve it
var viewModels = ss.Select(s =>
{
switch (s.SectionType)
{
case Enums.SectionTypes.OnlyText:
return (Section)new TextSection((TextSectionDTO) s);
case Enums.SectionTypes.OnlyImage:
return new ImageSection((ImageSectionDTO) s);
default:
throw new Exception("This section does not exist - FIXME");
}
}).ToList();
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