2016-02-04 29 views
6

Próbuję utworzyć listę ViewModels z DTO, dzwoniąc do wyboru na liście DTO. Jednak kompilator daje mi powiedzenie o błędzie:Dlaczego kompilator nie może wywnioskować typu dla tego połączenia wybierającego?

Argumenty typu dla metody nie można wywieść z wykorzystaniem spróbować określając argumenty typu

Moje pytanie brzmi, dlaczego nie może? Zarówno TextSectionDTO, jak i ImageSectionDTO pochodzą z SectionDTO. Próbuję utworzyć List z Sections, a zarówno TextSection i ImageSection pochodzą z Section.

Wiem, że to pytanie jest podobne do innych pytań opublikowanych tutaj, ale nie byłem w stanie znaleźć tam odpowiedzi.

To jest mój kod:

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; 
} 

Kiedy zmienić typy tak, że tylko zaakceptować nadrzędnej SectionDTO i tylko powrócić odcinek (robię im zarówno normalne zajęcia w tym scenariuszu) select działa jak wy mieliście oczekiwać. Wtedy, gdy zmienię typy tylko na TextSectionDTO i TextSection (zmieniając abstrakty), wybór już nie działa.

Chciałbym znaleźć rozwiązanie, dzięki któremu będę mógł pracować z konstrukcją, którą mam teraz, chociaż bardziej interesuje mnie, dlaczego to nie działa tak, jak jest. Nawet jeśli uda mi się to osiągnąć, prawdopodobnie później to zmienię.

Uwaga:

  • jestem kierowania MVC 4.5 (więc kompilator nie jest jakaś stara wersja nie jest w stanie wywnioskować, co było rozwiązaniem niektórych podobnych pytań na tutaj).
  • Klauzula przełączania ma domyślny przypadek, tj. Błąd nie powinien być spowodowany przez ścieżkę, która nie zwraca wartości.

Odpowiedz

6
case Enums.SectionTypes.OnlyText: 
    return new TextSection((TextSectionDTO) s); 
case Enums.SectionTypes.OnlyImage: 
    return new ImageSection((ImageSectionDTO) s); 

Te dwa przypadki powrotu tarasowy typy. Kompilator isnt wystarczająco inteligentny, aby sprawdzić, czy te typy wywodzą się z tego samego podstawowego typu więc trzeba rzucać je jawnie:

case Enums.SectionTypes.OnlyText: 
    return (SectionDTO) new TextSection((TextSectionDTO) s); 
case Enums.SectionTypes.OnlyImage: 
    return (SectionDTO) new ImageSection((ImageSectionDTO) s); 

Dlaczego isnt realizowane na kompilator? Zakładam, że dzieje się tak dlatego, że kompilator musi sprawdzać wiele różnych typów. Załóżmy, że twoje dwa typy: Foo1 i Foo2 nie pochodzą bezpośrednio z Bar, ale z dwóch różnych (odpowiednio: Bar1 i Bar2), które same dziedziczą od Bar. Teraz kompilator powinien sprawdzić, czy Foo1 i Foo2 można przypisać do dowolnej wspólnej klasy bazowej, której nie mogą, a także sprawdzić, czy pochodzą z czegoś, co ma wspólną klasę podstawową (Bar). W końcu musieliśmy sprawdzić cały łańcuch dziedziczenia aż do object, nie wspominając o żadnych interfejsach, które również powinny być sprawdzone.

class Foo1 : Bar1 {} 
class Foo2 : Bar2 {} 

class Bar1 : Bar {} 
class Bar2 : Bar {} 
+0

Dobra, to działało, wklejone „w sekcji” za obu zwrotów. Dlaczego jednak muszę to zrobić? Czy nie powinien być wystarczający fakt, że obydwoje wywodzą się z sekcji, biorąc pod uwagę fakt, że zwracam listę sekcji w metodzie, w której znajduje się wyrażenie? – Glubus

+1

Przypuszczam, że dzieje się tak dlatego, że gdyby kompilator sprawdził cały łańcuch dziedziczenia, może to zająć kilka pętli na całym łańcuchu. Assuem miałbyś dużo głębszy łańcuch. Teraz kompilator powinien sprawdzić wszystkie interfejsy i klasy bazowe wspomniane w całym łańcuchu, które mogą zająć zbyt dużo czasu. – HimBromBeere

+1

Obaj dziedziczą również z 'obiektu'. –

3

Select sposób ma dwa rodzaje aguments - TSource i TResult.Ponieważ wywołujesz to na IEnumerable<SectionDTO>, TSource jest rozumiane jako SectionDTO, więc ss.Select((SectionDTO s) => nie jest potrzebne i może być tylko ss.Select(s => ....

Problem to TResult, którego w twoim przypadku nie można wywnioskować. Czemu? Masz dwa zwroty - TextSection i ImageSection. Nie są one takie same i żadna z nich nie jest podstawą drugiej. Jak myślisz, co kompilator powinien wywnioskować? Myślisz, że odpowiedź powinna być wspólnym typem bazowym: Section, ale to samo może dotyczyć wspólnej bazy object lub dowolnej wspólnej klasy bazowej/interfejsu dwóch typów. Innymi słowy, wynik jest niejednoznaczny, więc zamiast zgadywać, jaki jest twój zamiar, kompilator wymaga, abyś wyraźnie to określił. Podobny do potrójnego operatora ? : byłoby wystarczające, aby określić wspólną bazę w jednym oddziale, więc dodaje powinno rozwiązać to

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(); 
+0

To wyjaśnienie jest bardzo przydatne, rozumiem to teraz. Dzięki. – Glubus