2012-01-04 95 views
4

我有我想要一個抽象類,接口,這樣陣列協方差在C#泛型列表

abstract class AnimalProcessor { 
    public abstract IList<Animal> ProcessResults(); 
} 

然後返回一些具體實例的實例

class GiraffeProcessor : AnimalProcessor { 
    public override IList<Animal> ProcessResults() { 
     return new List<Giraffe>(); 
    } 
} 

class LionProcessor : AnimalProcessor { 
    public override IList<Animal> ProcessResults() { 
     return new List<Lion>(); 
    } 
} 

的問題是,具體的類需要有相同的簽名覆蓋ProcessResults()方法,所以他們需要返回一個IList<Animal>,但是我想返回的ACTUAL數據是IList<Lion>,IList<Giraffe>等,但是那麼調用代碼必須做

GiraffeProcessor processor = new GiraffeProcessor(); 
IList<Animal> results = processor.GetResults(); 

哪個不給我一個Ilist,這是我想要的。

問題

1)上面的代碼不能編譯。 giraffeProcessor必須返回一個具體的List<Animal>,你可以用Giraffe對象填充它,但是你構造返回的對象類型必須是List<Animal>。不理想。

2)當您返回結果時,您只能得到IList<Animal>,而不是IList<Giraffe>。我試圖明確地IList<Giraffe>IList<Giraffe> results = (IList<Giraffe>) processor.GetResults(); 它給出了一個運行時錯誤,大概是因爲返回的對象不是IList<Giraffe>,它是一個IList<Animal>其中包含Giraffe對象。

任何人都可以建議我做錯了這裏與我的設計,因爲我有點難住最好的方式來完成這一點。

+1

有一個空白的IList服務,有一個類型的IList不服務的目的是什麼? – 2012-01-04 11:39:58

回答

6

如何:

abstract class AnimalProcessor<T> where T : Animal { 
    public abstract IList<T> ProcessResults(); 
} 

class GiraffeProcessor : AnimalProcessor<Giraffe> { 
    public override IList<Giraffe> ProcessResults() { 
     return new List<Giraffe>(); 
    } 
} 

class LionProcessor : AnimalProcessor<Lion> { 
    public override IList<Lion> ProcessResults() { 
     return new List<Lion>(); 
    } 
} 
+0

完美!只是我正在尋找的解決方案,並沒有意識到繼承父類的聲明以類似的泛型傳遞。非常感謝 – NZJames 2012-01-04 14:58:36

1

您也可以用一個泛型類型約束,例如宣佈AnimalProcessor解決此

public abstract class AnimalProcessor<T> where T : Animal 
{  
    public abstract IList<T> ProcessResults(); 
} 

如果不工作,你可以使用LINQ轉換運算,例如:

public class GiraffeProcessor : AnimalProcessor 
{  
    public override IList<Animal> ProcessResults() 
    {   
     return new List<Giraffe>().Cast<Animal>(); 
    } 
} 

或者存儲列表內的動物,但長頸鹿添加到它,例如

public class GiraffeProcessor : AnimalProcessor 
{  
    private List<Giraffe> _innerList = new List<Giraffe>(); 
    public override IList<Animal> ProcessResults() 
    {   
     return new List<Animal>(innerList);  } 
} 

最好的問候,

+1

標準'Cast ()'方法將返回一個'IEnumerable ',而不是'IList ' – 2012-01-04 11:48:00

+0

是的,非常真實。調用.Cast ().ToList()效率不高。我喜歡首先使用泛型類型的基類,或者第二次將innerlist作爲不同的類型存儲。問候, – 2012-01-04 13:36:38

1

如果您正在使用C#4.0中,你可以問問自己的處理器是否應該返回IEnumerable<T>而非IList<T>。如果答案是「是」,那麼你可以從協方差中獲利:

abstract class AnimalProcessor { 
    public abstract IEnumerable<Animal> ProcessResults(); 
} 

class GiraffeProcessor : AnimalProcessor { 
    public override IEnumerable<Animal> ProcessResults() { 
     return new List<Giraffe>(); 
    } 
} 

class LionProcessor : AnimalProcessor { 
    public override IEnumerable<Animal> ProcessResults() { 
     return new List<Lion>(); 
    } 
} 

這裏有幾個優點。首先,你可以實現這些作爲迭代塊:

class GiraffeProcessor : AnimalProcessor { 
    public override IEnumerable<Animal> ProcessResults() { 
     yield break; 
    } 
} 

二,少平凡,你允許客戶端代碼,以決定什麼樣採集到動物轉儲到 - 如果有的話。例如,考慮到消費者可能想要一個LinkedList<Animal>

var animals = new LinkedList<Animal>(animalProcessor.ProcessResults()); 

或者認爲客戶可能只需要遍歷序列:

foreach (var animal in animalProcessor.ProcessResults()) 
    { /*... do something ...*/ } 

在這兩種情況下,如果你使用的是ToList()通話在ProcessResults中,你會創建一個無用的列表。如果消費者真的想要一個List<Animal>,可以很容易地完成:

var animals = new List<Animal>(animalProcessor.ProcessResults()); 

最後,你也可以從一般的方法中獲益,即使你改變了方法的返回值的接口類型:

abstract class AnimalProcessor<T> where T : Animal { 
    public abstract IEnumerable<T> ProcessResults(); 
} 

class GiraffeProcessor : AnimalProcessor<Giraffe> { 
    public override IEnumerable<Giraffe> ProcessResults() { 
     yield break; 
    } 
} 

class LionProcessor : AnimalProcessor<Lion> { 
    public override IEnumerable<Lion> ProcessResults() { 
     return Enumerable.Empty<Lion>(); 
    } 
} 
+0

謝謝。只是一個關於列表的問題,我很喜歡使用List <>。ForEach(a => action())方法,但似乎每次我得到一個IEnumerable時,我必須先做.ToList ()之前做.ForEach )。我使用這個錯誤嗎? – NZJames 2012-01-05 13:47:39

+0

是的。您的ToList()調用會分配一個數組並將所有項目複製到其中,這是不必要的。你可以簡單地實現你自己的擴展方法來允許'IEnumerable '這個語法,但是正如Eric Lippert寫的那樣,它在哲學上是有問題的。請參閱http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx。 (在回覆對這篇文章的評論時,他指出,他也受到了列表.ForEach()')的哲學困擾。) – phoog 2012-01-05 14:58:31