【问题标题】:Cast interface to concrete type将接口转换为具体类型
【发布时间】:2016-06-10 15:04:01
【问题描述】:

我在我的项目中使用 GeoJSON.NET 库。在某些时候,我需要更新数据库中的功能。作为其中的一部分,我需要访问所述特征的坐标,以便将该信息也存储在数据库中。但是查看 GitHub 上的源代码,Feature 类具有 Geometry 属性作为 IGeometryObject:

 public IGeometryObject Geometry { get; set; }

根据 GeoJSON 规范存在多种“形状”,例如“多边形”、“圆形”、“点”等。这些特定形状已在 GeoJSON.NET 项目中设置。

在这些具体类型中,我可以真正挖掘并访问各种坐标。

目前我有这个:

    public int CreateFeature(Feature feature)
    {
        int featureId = 0;
        var coordinatesDt = new DataTable();
        coordinatesDt.Columns.Add("Latitude");
        coordinatesDt.Columns.Add("Longitude");

        //we are loading a datatable with the coordinates.  This gets passed to a SQL server stored procedure as a single parameters to insert
        //all the nodes.
        LineString lineString = ((Polygon)feature.Geometry).Coordinates[0];
        foreach (var coordinate in lineString.Coordinates)
        {
            var row = coordinatesDt.NewRow();
            row["Latitude"] = ((GeographicPosition)coordinate).Latitude;
            row["Longitude"] = ((GeographicPosition) coordinate).Longitude;
            coordinatesDt.Rows.Add(row);
        }


        using (SqlConnection conn = new SqlConnection(_smmConnectionString))
        using (SqlCommand cmd = new SqlCommand("dbo.MyProc", conn))
        {
            //set up params, set up TVP and execute...
        }
        return featureId;
}

这是 Polygon 类的一个 sn-p:

public List<LineString> Coordinates { get; set; }

因此,在我的代码中,我实际上是在对 Polygon 进行显式向下转换,因为我需要访问 Polygon 类的 Coordinates 成员。我知道我这样做是安全的,仅基于这些是我在我的应用程序中使用的唯一“类型”形状,即使我知道这不一定是最佳实践。但是,将来如果我们要使用其他类型,这将完全崩溃。我可以使用“is 或 as”来实现类型检查,但这仍然不能让我摆脱不得不向下转型的想法。

所以我的问题是最好的方法是什么?我已经阅读了有关为什么使用接口以及所有这些作为成员和/或参数的原因,并且必须进行明确的向下转换是“通常”不好的做法并且遵循不好的设计模式......除了极少数情况。那么我是属于“罕见情况”还是有更好的方法来解决这个问题?

【问题讨论】:

    标签: c# .net interface casting geojson


    【解决方案1】:

    基于 GeoJSON.NET API,我认为您确实属于这些“罕见情况”之一。你有几个选项来处理这个问题:

    1. 向您的 CreateFeature 方法添加一个 switch 语句,该方法调度到特定于形状的方法。将特定于形状的功能移动到特定于形状的方法中(见下文)。
    2. 创建一系列 if..else if 对特定类型(例如 if (shape is Polygon) ProcessShape((Polygon)shape);)进行类型检查,然后分派到特定于形状的方法。
    3. 使用访问者模式调度到特定于形状的方法(尽管这需要您修改 GeoJSON.NET 库的代码)。

    选项 1 的示例代码:

    switch (shape.Type)
    {
        case GeoJSONObjectType.Polygon:
            ProcessShape((Polygon)shape);
            break;
        // additional shape support here...
    }
    

    选项 3 的示例代码:

    // Add an AcceptVisitor method to the IGeometryObject interface:
    public interface IGeometryObject
    {
        GeoJSONObjectType Type { get; }
        void AcceptVisitor(IShapeProcessor visitor);
    }
    // Update each concrete shape class to include the new AcceptVisitor method:
    public void AcceptVisitor(IShapeProcessor visitor)
    {
        visitor.ProcessShape(this);
    }
    // Add an IShapeProcessor interface to the GeoJSON.NET project:
    public interface IShapeProcessor
    {
        void ProcessShape(Polygon shape);
        void ProcessShape(MultiPoint shape);
        void ProcessShape(LineString shape);
        // ...additional shape support...
    }
    // Update your existing class to implement the IShapeProcessor interface,
    // and then change your code to do something like:
    feature.Geometry.AcceptVisitor(this);
    

    ...如果您选择选项 3,请在 GitHub 上提交拉取请求,以便每个人都可以从您的改进中受益!

    【讨论】:

    • 感谢您的反馈!我非常感激。由于时间限制,以及形状类型的选择太少,我可能会坚持选项 1。
    • 分叉和更改外部 api 是一个非常糟糕的主意,因为它使维护变得更加困难(升级到新版本)。如果你走那条路,你也可以在界面中添加你想要的缺失函数:GetGeoPosition()
    • 我认为 GetGeoPosition() 和在每个具体类型中的设置将是这里的一个关键方面,并且不必过度设计获取这些坐标会有所帮助。谢谢!
    【解决方案2】:

    因此,您决定需要访问您无法更改的其他人接口的内部实现细节。

    那你真的别无选择,只能承担未来的维护费用。是的,您遇到了其中一种罕见的情况..

    为了最大限度地降低成本,我强烈建议您将所有危险代码包装在一个在已发布接口上运行的辅助类中,这样您就不会将其与您自己的业务逻辑混为一谈,并清楚地了解您将来需要在哪里进行更改.

    【讨论】:

    • 谢谢。你基本上说了@wablab 给出的例子。我需要另一双眼睛来告诉我,这实际上是那些罕见的案例之一。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    相关资源
    最近更新 更多