Написание метода расширения для типа T; Как я могу добавить ограничение типа для поля T? - PullRequest
3 голосов
/ 03 июля 2010

Исходная ситуация:

Я работаю с проприетарной платформой ( ESRI ArcGIS 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();

Проблема заключается в том, что как точечные, так и многоугольные формы(IPoint и IPolygon) упакованы в один и тот же тип (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>, определить метод расширения для этого типа оболочки, а затем каким-то образом превратить все объекты IFeature в этот тип оболочки?)

Ответы [ 5 ]

1 голос
/ 22 сентября 2011

Я думаю, что это плохой дизайн - добавлять метод расширения, который подходит только для точечных объектов интерфейса IFeature.Интерфейс IFeature реализуется всеми типами геометрий (точками, линиями и многоугольниками).Это означает, что метод расширения также должен быть спроектирован для поддержки всех типов геометрий при расширении интерфейса IFeature.Это явно не тот случай: -)

Когда вам нужно расширить IFeature, тогда проверьте тип фигуры во время выполнения, как вы написали.На мой взгляд, это лучшее решение вашей проблемы.

1 голос
/ 30 июля 2010

Я не думаю, что вы можете выполнить эту проверку во время компиляции с помощью интерфейса IFeature от ArcObjects.

Тип геометрии зависит от определения класса объектов, из которого загружен объект. Вы не узнаете об этом до времени выполнения.

1 голос
/ 03 июля 2010

По определению, если у вас есть объект IFeature, тогда его свойство Shape может содержать значение любого типа, реализующего IGeometry.Если вы контролируете создание экземпляров IFeature объектов, вы можете создать свой собственный универсальный класс, который реализует IFeature, или извлечь класс из базового класса, который реализует IFeature, а затем вы можете легко ограничить типShape.Если вы не управляете созданием этих объектов, то, вероятно, застряли с проверкой во время выполнения.

Если вы используете .NET 4.0, то можете использовать контракты кода.Статическая проверка выдаст вам предупреждение во время компиляции, если ваш метод расширения имеет предварительное условие для типа Shape.

1 голос
/ 03 июля 2010

Сделайте ваш интерфейс IFeature также универсальным:

IFeature<IPoint>
IFeature<IPolygon>

Затем вы можете задать консистент по внутреннему типу IFeature.

0 голосов
/ 03 июля 2010

Интересный вопрос, особенно для меня, поскольку я (должен) программировать с ArcObjects для жизни.

Редактировать, предупреждение: этот подход не работает.Это потерпит неудачу во время выполнения.Я оставлю это здесь для позора.

Принимая предложение Себастьяна П. Р. Гингтера о наследовании интерфейса и работая с ним, я определил интерфейс 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>
    }
}
...