Написание метода расширения для типа T; Как я могу добавить ограничение типа для поля T?
Исходная ситуация:
Я работаю с собственной платформой ( ArcGIS Engine от ESRI), которую я хочу расширить новыми функциональными возможностями. Я решил использовать методы расширения в 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 ответов
По определению, если у вас есть IFeature
объект тогда его Shape
свойство может содержать значение любого типа, который реализует IGeometry
, Если вы контролируете создание IFeature
объекты, то вы можете создать свой собственный общий класс, который реализует IFeature
или получить класс из базового класса, который реализует IFeature
и тогда вы можете легко ограничить тип Shape
, Если вы не управляете созданием этих объектов, вероятно, вы застряли с проверкой во время выполнения.
Если вы используете.NET 4.0, вы можете использовать контракты кода. Статическая проверка выдаст вам предупреждение во время компиляции, если ваш метод расширения имеет предварительное условие для типа Shape
,
Сделайте ваш интерфейс IFeature тоже универсальным:
IFeature<IPoint>
IFeature<IPolygon>
Затем вы можете установить консистент по внутреннему типу IFeature.
Я не думаю, что вы можете выполнить эту проверку во время компиляции с помощью интерфейса IFeature от ArcObjects.
Тип геометрии зависит от определения класса объектов, из которого загружен объект. Вы не будете знать это до времени выполнения.
Я думаю, что это плохой дизайн - добавлять метод расширения, который подходит только для точечных функций интерфейса IFeature. Интерфейс IFeature реализуется всеми типами геометрий (точки, линии и многоугольники). Это означает, что метод расширения также должен быть спроектирован для поддержки всех типов геометрий при расширении интерфейса IFeature. Это явно не тот случай:-)
Когда вам нужно расширить IFeature, проверьте тип фигуры во время выполнения, как вы написали. На мой взгляд, это лучшее решение для вашей проблемы.
Интересный вопрос, для меня тем более, что я (должен) программировать с 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>
}
}