如何在泛型方法中允许派生集合
How to allow derived collections in generic method
我有多个 类 表示对象,这些对象都具有由 Unity Rect 字段描述的边界。 (Zone, Room, Structure, Tunnel, Room
...)
这些对象通常放在集合中。 (List<Zone>, List<Room>
...)
我想要一个静态实用方法来测试其中一个是否与此类对象集合中的任何边界重叠,而无需使用 LINQ 转换列表。
public static bool BoundsOverlapsOtherBounds(Bounds bound, List<Bounds>)
我应该如何使用 C# 多态性、接口、协变来实现这一点,而不需要首先将 List<Room>
或 List<Zone>
转换为 List<Bounds>
?
到目前为止,我的尝试总是产生“无法将 X 转换为 Y” 编译器错误。
问题是 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)
应该接受 (Zone, Room, …)
中的任何 List
希望对您有所帮助
因为(隐含的)所有这些类型都已经从 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);
您甚至不必指定通用参数,因为它是推断出来的。
如果这些对象碰巧不共享一个共同的类型,那么它们很可能应该共享。继承是 a 解决方案,但我们不需要使用它来使类型具有共同的特征。有时这会让我们陷入困境。接口也可能有意义:
interface IHasBoundaries // not a great name?
{
Boundaries Bounds { get; }
}
这就是多态性。 多种形式(或类型)可以实现接口,您根本不关心它们的不同之处——只关心它们的共同点。您可以编写处理 IHasBoundaries
的代码,在这种情况下,您只需了解这些对象,即它们实现了接口。
那么您的方法如下所示:
bool BoundsOverlapsOtherBounds<T>(IHasBoundaries bound, List<T> bounds)
where T : IHasBoundaries
我有多个 类 表示对象,这些对象都具有由 Unity Rect 字段描述的边界。 (Zone, Room, Structure, Tunnel, Room
...)
这些对象通常放在集合中。 (List<Zone>, List<Room>
...)
我想要一个静态实用方法来测试其中一个是否与此类对象集合中的任何边界重叠,而无需使用 LINQ 转换列表。
public static bool BoundsOverlapsOtherBounds(Bounds bound, List<Bounds>)
我应该如何使用 C# 多态性、接口、协变来实现这一点,而不需要首先将 List<Room>
或 List<Zone>
转换为 List<Bounds>
?
到目前为止,我的尝试总是产生“无法将 X 转换为 Y” 编译器错误。
问题是 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)
应该接受 (Zone, Room, …)
List
希望对您有所帮助
因为(隐含的)所有这些类型都已经从 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);
您甚至不必指定通用参数,因为它是推断出来的。
如果这些对象碰巧不共享一个共同的类型,那么它们很可能应该共享。继承是 a 解决方案,但我们不需要使用它来使类型具有共同的特征。有时这会让我们陷入困境。接口也可能有意义:
interface IHasBoundaries // not a great name?
{
Boundaries Bounds { get; }
}
这就是多态性。 多种形式(或类型)可以实现接口,您根本不关心它们的不同之处——只关心它们的共同点。您可以编写处理 IHasBoundaries
的代码,在这种情况下,您只需了解这些对象,即它们实现了接口。
那么您的方法如下所示:
bool BoundsOverlapsOtherBounds<T>(IHasBoundaries bound, List<T> bounds)
where T : IHasBoundaries