编写T类型的扩展方法;如何为T字段添加类型约束?

时间:2010-07-03 10:07:07

标签: c# extension-methods typechecking arcobjects type-constraints

初始情况:

我正在使用专有框架(ESRIArcGIS Engine),我希望通过一些新功能进行扩展。我选择在C#中使用扩展方法。

下面显示的是与此问题相关的框架API部分:

    +------------------------+                   IGeometry
    |  IFeature <interface>  |                   <interface>
    +------------------------+                       ^
    |  +Shape: IGeometry     |                       |
    +------------------------+             +---------+---------+
                                           |                   |
                                        IPoint              IPolygon
                                        <interface>         <interface>

我想做什么:

我想为IFeature编写一个扩展方法,允许以下内容:

IFeature featureWithPointShape   = ...,
         featureWithPolygonShape = ...;

// this should work:
featureWithPointShape.DoSomethingWithPointFeature();

// this would ideally raise a compile-time error:
featureWithPolygonShape.DoSomethingWithPointFeature();

问题是点和多边形形状(IPointIPolygon)都包含在同一类型(IFeature)中,为此定义了扩展方法。扩展方法必须在IFeature上,因为我只能从IFeature转到IGeometry,反之则不然。


问题:

虽然可以在运行时轻松检查IFeature对象Shape的类型(请参阅下面的代码示例),但是如何在编译时实现此类型检查?

public static void DoSomethingWithPointFeature(this IFeature feature)
{
    if (!(feature.Shape is IPoint))
    {
        throw new NotSupportedException("Method accepts only point features!");
    }
    ...  // (do something useful here)
}

(是否有可能使用IFeature的通用包装类型,例如FeatureWithShape<IPoint>,在此包装类型上定义扩展方法,然后以某种方式转换所有{{1}对象到这个包装器类型?)

5 个答案:

答案 0 :(得分:1)

使您的IFeature界面也通用:

IFeature<IPoint>
IFeature<IPolygon>

然后你可以在IFeature的内部类型上设置一个constaint。

答案 1 :(得分:1)

根据定义,如果您有一个IFeature对象,则其Shape属性可以包含实现IGeometry的任何类型的值。如果你控制IFeature对象的实例化,那么你可以创建自己的通用类来实现IFeature或者从实现IFeature的框架类派生一个类然后你可以轻松约束Shape的类型。如果您无法控制这些对象的实例化,那么您可能会遇到运行时检查。

如果您正在使用.NET 4.0,那么您可以使用代码合同。如果您的扩展方法具有Shape类型的前提条件,则静态检查器会向您发出编译时警告。

答案 2 :(得分:1)

我认为您无法在编译时使用ArcObjects的IFeature接口实现此检查。

几何类型取决于加载要素的要素类的定义。直到运行时才会知道这一点。

答案 3 :(得分:1)

我认为添加一个扩展方法是一个糟糕的设计,它只适用于指向IFeature接口的功能。接口IFeature由所有类型的几何(点,线和多边形)实现。这意味着扩展方法还应设计为在扩展IFeature接口时支持所有类型的几何。事实显然不是这样: - )

当您必须扩展IFeature时,请在运行时检查形状类型,如您所写。在我看来,这是解决问题的最佳方案。

答案 4 :(得分:0)

有趣的问题,对我而言,特别是因为我(必须)用ArcObjects为生活而编程。

编辑,警告:此方法不起作用。它会在运行时失败。我会留在这里羞耻。

考虑到Sebastian P.R. Gingter建议的接口继承并运行它,我已经定义了一个继承IFeatureOf<T>的接口IFeature。这个新界面唯一有用的是在声明功能时添加更多信息。

但是,如果您事先知道要处理点要素,则可以将这些要素声明为IFeatureOf<IPoint>,并将它们提供给期望包含点几何的要素的函数。

您当然可以将面要素类中的要素声明为var notReallyAPointFeature = (IFeatureOf<IPoint>)myPolygonFeature;,但如果您事先知道要素类型并使用IFeatureOF<>来约束它,则会出现编译时错误你把它喂给专门的职能部门。

下面的小例子:

using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;

class Program
{
    public interface IFeatureOf<T> : IFeature { };

    public static void AcceptsAllFeatures(IFeature feature) {
        //do something, not caring about geometry type...
        return;
    }

    public static void AcceptsOnlyPointFeatures(IFeatureOf<IPoint> pointFeature) {
        IPoint pointGeometry = (IPoint)pointFeature.Shape; //constraint guarantees this is OK
        //do something with pointGeometry
        return;
    }

    static void Main(string[] args)
    {
        IFeature pointFeature = new FeatureClass(); //this is where you would read in a feature from your data set
        IFeature polylineFeature = new FeatureClass();
        var constainedPointFeature = (IFeatureOf<IPoint>)pointFeature;
        var constrainedPolylineFeature = (IFeatureOf<IPolyline>)polylineFeature;

        AcceptsAllFeatures(constainedPointFeature);             //OK
        AcceptsAllFeatures(constrainedPolylineFeature);         //OK
        AcceptsAllFeatures(pointFeature);                       //OK
        AcceptsAllFeatures(polylineFeature);                    //OK

        AcceptsOnlyPointFeatures(constainedPointFeature);       //OK

        AcceptsOnlyPointFeatures(constrainedPolylineFeature);   //Compile-time error: IFeatureOf<IPolyline> != IFeatureOf<IPoint>
        AcceptsOnlyPointFeatures(pointFeature);                 //Compile-time error: IFeature != IFeatureOf<something>
        AcceptsOnlyPointFeatures(polylineFeature);              //Compile-time error: IFeature != IFeatureOf<something>
    }
}