У меня есть несколько классов, представляющих объекты, все из которых имеют границы, описанные полем Прямоугольник единства. (Zone, Room, Structure, Tunnel, Room
...)
Эти объекты часто помещаются в коллекции. (List<Zone>, List<Room>
...)
Я хочу иметь единственный статический служебный метод, который будет проверять, перекрывает ли один из них какие-либо границы из коллекции таких объектов, без необходимости создания списка с использованием LINQ.
public static bool BoundsOverlapsOtherBounds(Bounds bound, List<Bounds>)
Как мне использовать полиморфизм С#, интерфейсы, ковариантность для достижения этого без необходимости сначала приводить List<Room>
или List<Zone>
к List<Bounds>
?
Мои попытки до сих пор всегда приводили к ошибкам компилятора Невозможно преобразовать X в Y".
Если Zone
, Room
и т. д. реализуют какой-то общий интерфейс, который выставляет прямоугольные границы, вы можете выполнить сравнение без приведения коллекции. bool BoundsOverlapsOtherBounds<T>(IHasBoundaries bound, IEnumerable<IHasBoundardies> others) where T : IHasBoundaries
.
Можете ли вы поделиться кодом для одного из ваших классов Zone
/Room
/…? Я предполагал, что все они наследуются от класса Bounds
, но, как всегда с предположениями, это может быть не так.
@ScottHannen, универсальный T
, ничего туда не добавляет, поскольку он не используется ни в одном из параметров. Imo его следует использовать в случае, если вы хотите использовать List
вместо IEnumerable
(IEnumerable
будет автоматически работать для общего родителя/интерфейса без необходимости использования дженериков). Итак, для List
это будет что-то вроде: BoundsOverlapsOtherBounds<T>(Bounds bound, List<T> others) where T : Bounds
. который, как я предполагаю, будет соответствовать решению, предложенному Шри Харшей
@Knoop - будет выведен общий аргумент. Что касается IEnumerable<T>
vs List
, это просто привычка. Я должен был просто использовать List
, чтобы не добавлять что-то еще сверху. В вопросе не указывалось, что все унаследовано от Bounds
. Но это то же самое в любом случае. Если это так, общее решение работает. Или, если вы просто реализуете какой-то интерфейс, раскрывающий границы, он будет работать точно так же.
@ScottHannen Дело в том, что вы не использовали T
ни в одном из параметров, что делало его бесполезным. Код из вашего комментария при использовании List
вместо IEnumerable
все равно не скомпилируется, если вы накормите его List<Room>
. В любом случае ответ, который вы добавили, был таким, каким я пытался сказать, что это нужно сделать, если вы хотите использовать дженерики. Вы правы, что в этом случае нет необходимости указывать T
в вызове, и он будет выведен, но я не это имел в виду.
Проблема в том, что List<Zone>
отличается от List<Bounds>
. Вы можете добавить Room
к List<Bounds>
, но не к List<Zone>
, поэтому их нельзя преобразовать. Однако я предполагаю, что вы хотите только перебрать список границ, а не изменять коллекцию, для этого вам нужен только IEnumerable
вместо List
. Поскольку IEnumerable<Zone>
действует так же, как IEnumerable<Bounds>
, это разрешено. Поэтому, если вы действительно хотите прочитать только элементы параметра bounds
, измените подпись на это:
public static bool BoundsOverlapsOtherBounds(Bounds bound, IEnumerable<Bounds> bounds)
который должен принимать любые List
из (Zone, Room, …)
Надеюсь это поможет
Поскольку (как подразумевается) все эти типы уже наследуются от Bounds
, вам не нужно приводить List<Room>
или List<Zone>
к List<Bounds>
.
Ты можешь это сделать:
bool BoundsOverlapsOtherBounds<T>(Bounds bound, List<T> bounds) where T : Bounds
Общее ограничение означает, что вы можете передать любой List<T>
методу, если T
реализует или наследует Bounds
.
Итак, если у вас есть List<Room>
, вы можете передать его методу без явного приведения:
var rooms = new List<Room>();
var otherBounds = new Bounds();
var overlaps = BoundsOverlapsOtherBounds(otherBounds, rooms);
Вам даже не нужно указывать общий аргумент, потому что он выводится.
Если по какой-то причине эти объекты не имеют общего типа, то, скорее всего, это тот случай, когда они должны. Наследование — это решение а, но нам не нужно использовать его, чтобы типы имели общие характеристики. Иногда это загоняет нас в угол. Интерфейс также может иметь смысл:
interface IHasBoundaries // not a great name?
{
Boundaries Bounds { get; }
}
Это полиморфизм. Несколько форм (или типы) могут реализовывать интерфейс, и вас совершенно не волнует, чем они отличаются — только то, что у них общего. Вы можете написать код, который имеет дело с IHasBoundaries
, и в этом контексте это единственное, что вам нужно знать об этих объектах, что они реализуют интерфейс.
Тогда ваш метод выглядит так:
bool BoundsOverlapsOtherBounds<T>(IHasBoundaries bound, List<T> bounds)
where T : IHasBoundaries
Можешь попробовать
List<T>