2013-10-21 61 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 
{ 
    // ... 
}