2016-02-04 19 views
6

Sto provando a creare un elenco di ViewModels da un DTO chiamando una selezione nell'elenco di DTO. Tuttavia, il compilatore mi dà un detto errore:Perché il compilatore non può inferire il tipo per questa chiamata selezionata?

Gli argomenti di tipo per metodo non può essere dedotta dal l'utilizzo provare a specificare gli argomenti di tipo

La mia domanda è, perché non è vero? Entrambi TextSectionDTO e ImageSectionDTO derivano da SectionDTO. Sto cercando di creare un List di Sections e entrambi TextSection e ImageSection derivano da Section.

So che questa domanda è vicina ad alcune altre domande pubblicate qui, ma non sono riuscito a trovare una risposta.

Questo è il mio codice:

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

Quando cambio i tipi in modo che accetto solo SectionDTO superclasse e restituire solo la sezione (li faccio entrambe le classi normali in questo scenario) le opere selezionate come ci si aspettarsi. Quindi quando cambio i tipi solo in TextSectionDTO e TextSection (cambiando gli abstract indietro), la selezione non funziona più.

Mi piacerebbe una soluzione in modo che io possa farlo funzionare con la costruzione che ho adesso, anche se sono più interessato al motivo per cui questo non funziona così com'è. Anche se riuscirò a farlo funzionare, probabilmente lo rifatterò più tardi.

Nota:

  • ho scelto come target MVC 4.5 (in modo che il compilatore non è una vecchia versione non essere in grado di dedurre, che era la soluzione ad alcune domande simili qui).
  • La clausola switch ha un caso predefinito, ovvero l'errore non dovrebbe essere causato da un percorso che non restituisce un valore.

risposta

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

Questi due casi ritorno tipi differen. Il compilatore selezionata purtroppo non abbastanza intelligente per controllare se questi tipi derivano dalla stessa base-tipo in modo da avere a lanciare loro esplicitamente:

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

Perchè questo purtroppo non si implementato sul compilatore? Presumo questo perché il compilatore deve controllare molti tipi diversi. Si supponga che i due tipi Foo1 e Foo2 non derivino direttamente da Bar ma da due diversi (Bar1 e Bar2 di conseguenza) ereditati da Bar. Ora il compilatore dovrebbe controllare se Foo1 e Foo2 possono essere assegnati a qualsiasi comune classe base che non possono, e anche controllare se derivano da qualcosa che HA una classe base comune (Bar). Alla fine abbiamo dovuto controllare l'intera catena ereditaria fino allo object, senza menzionare alcuna interfaccia che dovrebbe essere controllata.

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

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

Va bene, questo ha funzionato, incollato 'come sezione' dietro entrambi i rendimenti. Perché devo fare questo però? Non dovrebbe essere sufficiente il fatto che entrambi derivino dalla Sezione dato il fatto che sto restituendo una lista di sezioni nel metodo in cui si trova l'espressione? – Glubus

+1

Suppongo che ciò sia dovuto al fatto che se il compilatore verifica l'intera catena di ereditarietà, questo potrebbe richiedere diversi loop sull'intera catena. Assuem hai una catena molto più profonda. Ora il compilatore dovrebbe verificare tutte le interfacce e le classi di base menzionate nell'intera catena, il che potrebbe richiedere troppo tempo. – HimBromBeere

+1

Entrambi ereditano anche da 'object'. –

3

Select metodo ha due tipo aguments - TSource e TResult.Poiché si sta invocando su IEnumerable<SectionDTO>, TSource viene considerato come SectionDTO, quindi ss.Select((SectionDTO s) => non è necessario e può essere solo ss.Select(s => ....

Il problema è il TResult che nel tuo caso non può essere dedotto. Perché? Hai due resi: TextSection e ImageSection. Non sono la stessa cosa e nessuno di loro è una base dell'altro. Cosa pensi che il compilatore dovrebbe dedurre? Pensi che la risposta dovrebbe essere il tipo di base comune Section, ma lo stesso potrebbe valere per una base comune object o qualsiasi classe base/interfaccia comune dei due tipi. In altre parole, il risultato è ambiguo, quindi invece di indovinare qual è la tua intenzione, il compilatore richiede di specificarlo esplicitamente. Simile a operatore ternario ? : sarebbe sufficiente per specificare la base comune in un solo ramo, quindi il seguente dovrebbe risolverlo

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

Questa spiegazione è molto utile, lo capisco ora. Grazie. – Glubus