传入并返回自定义数据 - 接口是否正确?

时间:2017-01-05 13:44:44

标签: c# generics interface

我正在C#库中编写代码以在(二维)数据集上执行clustering - 基本上将数据分成组或集群。为了有用,库需要接收“通用”或“自定义”数据,对其进行聚类并返回聚簇数据。

要做到这一点,我需要假设传入的数据集中的每个数据都有一个与之关联的2D向量(在我的情况下为Q_ASSERT(QStringLiteral("foo%1baz").arg("bar") == QStringLiteral("foobarbaz")); Lat - 我正在使用纵坐标)。

我的第一个想法是使用泛型类型,并传入两个列表,一个通用数据列表(即Lng)和另一个指定2D向量的相同长度(即List<T>,其中List<Coordinate>是我的用于指定lat,lng对的类,其中列表通过索引彼此对应。但这是相当繁琐的,因为这意味着在算法中我必须以某种方式跟踪这些指数。

我的下一个想法是使用界面,我在其中定义界面

Coordinate

并确保我传入的数据实现此接口(即我可以假设传入的每个数据都有public interface IPoint { double Lat { get; set; } double Lng { get; set; } } Lat)。

但这也不适合我。我正在使用我的C#库来集中传输网络中的停靠点(在不同的项目中)。该类称为Lng,此类也来自外部库,因此我无法实现该类的接口。

我所做的是继承自Stop,创建一个名为Stop的类,如下所示:

ClusterableStop

如您所见,实现了public class ClusterableStop : GTFS.Entities.Stop, IPoint { public ClusterableStop(Stop stop) { Id = stop.Id; Code = stop.Code; Name = stop.Name; Description = stop.Description; Latitude = stop.Latitude; Longitude = stop.Longitude; Zone = stop.Zone; Url = stop.Url; LocationType = stop.LocationType; ParentStation = stop.ParentStation; Timezone = stop.Timezone; WheelchairBoarding = stop.WheelchairBoarding; } public double Lat { get { return this.Latitude; } } public double Lng { get { return this.Longitude; } } } 接口。现在我使用IPoint的构造函数首先将数据集中的所有ClusterableStop转换为Stop,然后运行算法并将结果作为ClusterableStop s。

这不是我想要的,因为我想根据它们所属的集群对ClusterableStop做些什么。我不能这样做,因为我实际上已经实例化 new 停止,即Stop s !!

我仍然可以实现我想要的,因为例如我可以通过Id检索原始对象。但肯定有更优雅的方式来完成所有这些?这是使用接口的正确方法吗?这似乎是一个简单的想法 - 传入并获取自定义数据 - 但结果却变得如此复杂。

2 个答案:

答案 0 :(得分:4)

由于您只需要将(纬度,经度)对与2D数组的每个元素相关联,您可以创建一个带有委托的方法,该委托为每个数据生成相关位置,如下所示:

ClusterList Cluster<T>(IList<T> data, Func<int,Coordinate> getCoordinate) {
    for (int i = 0 ; i != data.Count ; i++) {
        T item = data[i];
        Coordinate coord = getCoord(i);
        ...
    }
}

现在由调用者决定Coordinate与每个数据元素的配对方式。

请注意,按列表位置关联并非您可用的唯一选项。另一种选择是传递一个接受该项的委托,并返回其坐标:

ClusterList Cluster<T>(IEnumerable<T> data, Func<T,Coordinate> getCoordinate) {
    foreach (var item in data) {
        Coordinate coord = getCoord(item);
        ...
    }
}

虽然这种方法比基于索引的方法更好,但是当坐标在对象本身上不可用时,它需要调用者在T上保留某种关联容器,这必须要么使用基于散列的容器,或者是IComparable<T>。第一种方法对T没有限制。

在您的情况下,第二种方法更可取:

var clustered = Cluster(
    myListOfStops
,   stop => new Coordinate(stop.Latitude, stop.Longitude)
);

答案 1 :(得分:2)

您是否考虑过使用Tuples来完成工作 - 有时这是一种在不创建全新类的情况下关联两个类的有用方法。您可以创建元组列表:

List<Tuple<Point, Stop>>

其中Point是你聚集的东西。