2016-02-04 41 views
6

我正在嘗試通過調用DTO列表中的select來創建DTO列表中的ViewModel。 然而,編譯器給了我一個錯誤說:爲什麼編譯器不能推斷這個select調用的類型?

的方法類型參數不能從使用推斷嘗試指定類型參數

我的問題是,爲什麼不可以呢? TextSectionDTOImageSectionDTO均源自SectionDTO。我正在嘗試創建ListSections,並且TextSectionImageSection均源自Section

我知道這個問題已經接近發佈在這裏的一些其他問題,但我無法在那裏找到答案。

這是我的代碼:

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

當我改變的類型,這樣我只接受超SectionDTO只有返回科(我讓他們正常班在這種情況下),選擇的作品就像你期望。然後,當我將類型更改爲僅TextSectionDTO和TextSection(將摘要更改回)時,選擇不再起作用。

我想要一個解決方案,以便我可以使用我現在的構造來工作,儘管我更感興趣的是爲什麼這種方式無法實現。即使我可以把它工作,我可能會在稍後重構。

注:

  • 我針對MVC 4.5(所以編譯器是不是有些不能夠推斷出,這是解決一些類似的問題在這裏的老版本)。
  • switch子句有一個默認情況,即錯誤不應該由不返回值的路徑引起。

回答

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

這兩種情況返回differen類型。 isn't足夠聰明的編譯器檢查是否這些類型從相同的基類型派生的,所以你必須明確地丟掉。

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

爲什麼在編譯器實現這個isn't?我認爲這是因爲編譯器必須檢查許多不同的類型。假定您的兩種類型Foo1Foo2不直接從Bar派生,而是從兩個不同的派生(Bar1Bar2相應),它們自己從Bar繼承。現在,編譯器應該檢查Foo1Foo2是否可以分配給它們不能使用的任何公共基類,並且還要檢查它們是否來自具有共同基類(Bar)的東西。最後,我們必須檢查整個繼承鏈,直到object,而不是提及任何應該檢查的接口。

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

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

好吧,這工作,粘貼「作爲第」既回報落後。爲什麼我需要這樣做呢?不應該因爲我在表達式所在的方法中返回一個節的列表這一事實而導致它們都來自Section的事實嗎? – Glubus

+1

我想這是因爲如果編譯器會檢查整個繼承鏈,這可能會在整個鏈上需要幾個循環。 Assuem你有一個更深的鏈。現在編譯器應該檢查整個鏈中提到的所有接口和基類,這可能需要很長時間。 – HimBromBeere

+1

它們都從'object'繼承。 –

3

Select方法有兩個類型aguments - TSourceTResult。由於您在IEnumerable<SectionDTO>上調用它,所以TSource被推斷爲SectionDTO,因此不需要ss.Select((SectionDTO s) =>,並且可以只是ss.Select(s => ...

問題是TResult在你的情況不能推斷。爲什麼?你有兩個回報 - TextSectionImageSection。它們不是一樣的,它們都不是另一個的基礎。你認爲編譯器應該推斷什麼?你認爲答案應該是通用基類型Section,但是同樣可以應用於通用基類object或這兩種類型的任何通用基類/接口。換句話說,結果是不明確的,所以編譯器不需要猜測你的意圖是什麼,而是需要你明確地指定它。類似三元運算? :就足夠指定在短短的一個分支的共同的基礎,所以下面應該解決它

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

這個解釋也非常有用,我現在明白了。謝謝。 – Glubus

相關問題