简体   繁体   中英

How to allow derived collections in generic method

I have multiple classes representing objects which all have bounds described by a Unity Rect field. ( Zone, Room, Structure, Tunnel, Room ...)

These objects are often placed in collections. ( List<Zone>, List<Room> ...)

I want to have single static utility method that will test if a single one of these overlaps any the bounds from a collection of such objects, without having to cast a List using LINQ.

public static bool BoundsOverlapsOtherBounds(Bounds bound, List<Bounds>)

How should I use C# polymorphism, interfaces, covariance to achieve this, without needing to cast List<Room> or List<Zone> to List<Bounds> first?

My attempts so far have always produced " Cannot covert X to Y" compiler errors.

Because (as implied) all of these types already inherit from Bounds , you don't need to cast List<Room> or List<Zone> to List<Bounds> .

You can do this:

bool BoundsOverlapsOtherBounds<T>(Bounds bound, List<T> bounds) where T : Bounds

The generic constraint means that you can pass any List<T> to the method as long as T implements or inherits Bounds .

So if you have a List<Room> you can pass it to the method without explicitly casting it:

var rooms = new List<Room>();
var otherBounds = new Bounds();
var overlaps = BoundsOverlapsOtherBounds(otherBounds, rooms);

You don't even have to specify the generic argument because it's inferred.


If by any chance these objects don't share a common type, then it's likely a case where they should. Inheritance is a solution, but we don't need to use it to cause types to have a common characteristic. Sometimes that paints us into a corner. An interface might also make sense:

interface IHasBoundaries // not a great name?
{
    Boundaries Bounds { get; }
}

That's polymorphism. Multiple forms (or types) can implement the interface, and you don't care at all about what makes them different - only what they have in common. You can write code that deals with IHasBoundaries and in that context that's the only thing you need to know about those objects, that they implement the interface.

Then your method looks like this:

bool BoundsOverlapsOtherBounds<T>(IHasBoundaries bound, List<T> bounds) 
    where T : IHasBoundaries

The problem is that a List<Zone> is different then a List<Bounds> . You can add a Room to a List<Bounds> but not to a List<Zone> which is why they cannot be converted. However I assume you only want to iterate the list of bounds and not change the collection, for this you only need an IEnumerable instead of a List . Since an IEnumerable<Zone> functions the same as an IEnumerable<Bounds> this is allowed. So if you indeed only want to read the elements of the bounds parameter, change the signature to this:

public static bool BoundsOverlapsOtherBounds(Bounds bound, IEnumerable<Bounds> bounds)

that should accept any List of (Zone, Room, …)

Hope this helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM