【问题标题】:How to allow derived collections in generic method如何在泛型方法中允许派生集合
【发布时间】:2019-07-05 09:36:17
【问题描述】:

我有多个表示对象的类,它们都有Unity Rect 字段描述的边界。 (Zone, Room, Structure, Tunnel, Room...)

这些对象通常放在集合中。 (List<Zone>, List<Room>...)

我想要一个静态实用程序方法来测试其中一个是否与此类对象集合中的任何边界重叠,而不必使用 LINQ 强制转换列表。

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

我应该如何使用 C# 多态性、接口、协方差来实现这一点,而不需要先将 List&lt;Room&gt;List&lt;Zone&gt; 转换为 List&lt;Bounds&gt;

到目前为止,我的尝试总是产生“无法将 X 转换为 Y” 编译器错误。

【问题讨论】:

  • 你可以试试List&lt;T&gt;
  • 如果ZoneRoom 等实现了一些公开矩形边界的通用接口,那么您可以在不强制转换集合的情况下执行比较。 bool BoundsOverlapsOtherBounds&lt;T&gt;(IHasBoundaries bound, IEnumerable&lt;IHasBoundardies&gt; others) where T : IHasBoundaries.
  • 您能分享一下您的Zone/Room/... 课程的代码吗?我假设它们都继承自 Bounds 类,但与往常一样,情况可能并非如此。
  • @ScottHannen T 泛型不会在其中添加任何内容,因为它没有在任何参数中使用。如果您想使用List 而不是IEnumerable,则应该使用它(IEnumerable 将自动用于共享父/接口,而无需泛型)。所以对于List,它类似于:BoundsOverlapsOtherBounds&lt;T&gt;(Bounds bound, List&lt;T&gt; others) where T : Bounds。我认为这将与 sri harsha 提出的解决方案类似
  • @Knoop - 将推断出通用参数。至于IEnumerable&lt;T&gt; vs List,这只是一种习惯。我应该只使用List 以避免在顶部引入其他内容。该问题没有指定继承自Bounds 的所有内容。但无论哪种方式都是一样的。如果是这样,则通用解决方案有效。或者,如果您只是实现一些暴露边界的接口,它的工作方式完全相同。

标签: c# collections casting covariance


【解决方案1】:

因为(如暗示的那样)所有这些类型都已从 Bounds 继承,因此您无需将 List&lt;Room&gt;List&lt;Zone&gt; 强制转换为 List&lt;Bounds&gt;

你可以这样做:

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

泛型约束意味着您可以将任何List&lt;T&gt; 传递给方法,只要T 实现或继承Bounds

因此,如果您有 List&lt;Room&gt;,则可以将其传递给方法而无需显式转换:

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

【讨论】:

    【解决方案2】:

    问题在于List&lt;Zone&gt;List&lt;Bounds&gt; 不同。您可以将Room 添加到List&lt;Bounds&gt;,但不能添加到List&lt;Zone&gt;,这就是它们无法转换的原因。但是我假设您只想迭代边界列表而不更改集合,为此您只需要IEnumerable 而不是List。由于IEnumerable&lt;Zone&gt; 的功能与IEnumerable&lt;Bounds&gt; 相同,因此这是允许的。因此,如果您确实只想读取 bounds 参数的元素,请将签名更改为:

    public static bool BoundsOverlapsOtherBounds(Bounds bound, IEnumerable&lt;Bounds&gt; bounds)

    应该接受(Zone, Room, …)中的任何List

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多