Question

Initial situation:

I am working with a proprietary framework (ESRI's ArcGIS Engine) which I want to extend with some new functionality. I've chosen to use extension methods in C# for this.

Shown below are the parts of the framework API that are relevant to this question:

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

What I want to do:

I want to write an extension method for IFeature that will allow the following:

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

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

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

The problem is that both point and polygon shapes (IPoint and IPolygon) are wrapped in the same type (IFeature), for which the extension method is defined. The extension method has to be on IFeature because I can only get from an IFeature towards its IGeometry, but not vice versa.


Question:

While the type of an IFeature object's Shape can easily be checked at run-time (see code example below), how could I achieve this type check at compile-time?

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

(Is there possibly any way to use a generic wrapper type for IFeature, e.g. FeatureWithShape<IPoint>, define the extension method on this wrapper type, and then somehow turn all IFeature objects into this wrapper type?)

Was it helpful?

Solution

By definition, if you have an IFeature object then its Shape property can contain a value of any type that implements IGeometry. If you're in control of instantiation of the IFeature objects, then you can create your own generic class that implements IFeature or derive a class from a framework class that implements IFeature and then you can easily constrain the type of Shape. If you're not in control of instantiation of these objects, then you're probably stuck with a run-time check.

If you happen to be using .NET 4.0, then you could use code contracts. The static checker would give you a compile-time warning if your extension method had a pre-condition on the type of Shape.

OTHER TIPS

Make your IFeature interface generic too:

IFeature<IPoint>
IFeature<IPolygon>

Then you can set a constaint on the inner type of the IFeature.

I don't think you can achieve this check at compile time with the IFeature interface from ArcObjects.

The geometry type depends on the definition of the featureclass that the feature is loaded from. You won't know this until run-time.

I think it is a bad design to add an extension method which is only appropiate to point features to the IFeature interface. The interface IFeature is implemented by all types of geometries (points, lines ans polygons). This means the extension method should also be designed to support all types of geometries when extending the IFeature interface. This is clearly not the case :-)

When you have to extend IFeature, then check for the shape type at runtime, as you have written. In my eyes, that is the best solution for your problem.

Interesting question, for me especially since I (have to) program with ArcObjects for a living.

Edit, warning: this approach doesn't work. It'll fail at runtime. I'll leave it here for shame.

Taking Sebastian P.R. Gingter's suggestion of interface inheritance and running with it, I've defined an interface IFeatureOf<T> that inherits IFeature. The only thing this new interface is good for, is adding more information when declaring Features.

But if you know in advance that you're dealing with point features, then you can declare these features as IFeatureOf<IPoint> and feed them to functions that expect features containing a point geometry.

You can of course still declare a feature from a polygon feature class as var notReallyAPointFeature = (IFeatureOf<IPoint>)myPolygonFeature;, but if you know the feature type in advance and use IFeatureOF<> to constrain it, you will get compile time errors if you feed it to specialized functions.

Small example below:

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>
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top