泛型集合 C# 中的协方差

本文关键字:方差 集合 泛型 | 更新日期: 2023-09-27 18:36:22

我有一些类和接口

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

我不确定这是最优雅的解决方案。最尴尬的是调用方必须执行此转换

这类似于我在架构中的问题?还有更优雅的解决方案吗?

附言说来话长,但我真的无法给出所有类别中同一物种的坐标。

泛型集合 C# 中的协方差

那是因为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>只允许您循环访问集合,这就是它可以标记为协变的原因。

您可以将签名更改为:

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

定义显式(强制转换)或隐式运算符,用于获取List<User>并将其转换为List<IGeoObject>

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