2013-10-21 67 views
1

我有一些類和接口協方差在通用集合C#

public interface IGeoObject 
    { 
     GeoCoordinate GetGeocoordinate(); 
    } 

public class User : IGeoObject 
    { 
     public GeoCoordinate GetGeocoordinate() 
     { 
      //... 
      return GeoCoordinate;   
     } 
    } 

public class Venue : IGeoObject 
    {  
     public GeoCoordinate GetGeocoordinate() 
     { 
      //... 
      return GeoCoordinate; 
     } 
    } 

public class Place : IGeoObject 
    {  
     public GeoCoordinate GetGeocoordinate() 
     { 
      //... 
      return GeoCoordinate; 
     } 
    } 

我有,例如可變用戶類型List<User>

我有方法與簽名

double GetMinDistance(List<IGeoObject> objects, GeoCoordinate position) 

我可以不會將用戶傳遞給GetMinDistance,因爲泛型類型的協變不起作用。

我可以創建其他列表和使用方法.ConvertAll(或.CasT<T>):

List<IGeoObject> list = new List<User>(listUser).ConvertAll(x => (IGeoObject)x); 
var dist = GeoTest.GetMinDistance(list, position); 

我不知道,這是最完美的解決方案。最讓人尷尬的是來電者必須做這個轉換

這與我在架構中遇到的問題類似嗎?有更多優雅的解決方案?

P.S.長話短說,但我實在無法給出所有類別中同一物種的座標。

回答

8

那是因爲List<T>不是協變。

public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable 

正如你所看到的,有在類聲明沒有out關鍵字。您必須使用IEnumerable<T>來獲得協變支持。

public interface IEnumerable<out T> : IEnumerable 

這是因爲List<T>有一套方法,可以讓你修改集合,並與協方差,你會失去編譯這些操作的類型安全。 IEnumerable<T>只允許您迭代集合,這就是爲什麼它可以標記爲協變。

+0

在其他更糟的是,如果他改變了方法簽名爲'雙GetMinDistance(IEnumerable的對象,會有地理座標位置)'然後他可以通過'名單'。 –

+0

謝謝你,很好的回答! – Alexandr

1

您可以定義一個明確的(cast)或隱式運算符,它將List<User>並將其轉換爲List<IGeoObject>

public static explicit operator List<IGeoObject>(List<IGeoObject> ls) 
{ 
    return new ls.ConvertAll(x => (IGeoObject)x); 
} 
3

您可以將簽名改爲:

double GetMinDistance<TGeoObject>(List<TGeoObject> objects, GeoCoordinate position) 
    where TGeoObject : IGeoObject 
{ 
    // ... 
}