【问题标题】:Complex inheritance with generics使用泛型的复杂继承
【发布时间】:2015-09-28 12:51:56
【问题描述】:

假设我有一组类/接口:

class ObjectData { }
class UnitData : ObjectData { }
class Component1Data : UnitData { }
class Component2Data : UnitData { }

interface IObject { }
interface IUnit : IObject { }
interface IComponent1 : IUnit { }
interface IComponent2 : IUnit { }

abstract class Object<D, O, I>
    where D : ObjectData
    where O : Object<D, O, I>, I, new()
    where I : IObject
{ }

这里的主要兴趣点是Object 类,它是某些层次结构中的基本泛型类。类型参数“O”是一种指定从Object 派生的实际类的类型的方法。因此,可以在没有问题的情况下声明和编译这样的内容:

class Unit : Object<UnitData, Unit, IUnit>, IUnit { }

但我想做的是定义另一个从 Object 派生的通用“第二级”类,它也应该作为几个类似“第三级”实体的基类。它必须是非抽象的,因为它也是某种实体。所以我需要定义这样的东西:

class Unit<D, I> : Object<D, Unit<D, I>, I>, IUnit
    where D : UnitData
    where I : IUnit
{ }

class Component1 : Unit<Component1Data, IComponent1>, IComponent1 { }
class Component2 : Unit<Component2Data, IComponent2>, IComponent2 { }

它会产生以下编译错误:

error CS0311: The type 'Unit<D, I>' cannot be used as type parameter 'O' in the generic type or method 'Object<D, O, I>'. There is no implicit reference conversion from 'Unit<D, I>' to 'I'.

问题是为什么?在我看来,如果Unit&lt;D, I&gt; 正在实现IUnit,并且参数“I”被指定为where I : IUnit,那么一切都应该没问题。我就是这么看的。我没看到什么?

【问题讨论】:

  • 这一定是我见过的最复杂的泛型使用...
  • O 将如何在Object&lt;D, O, I&gt; 内部使用?请发布示例代码。
  • 例如这样: public static O Create(D data) { return data != null ?新 O { 数据 = 数据 } : null; } 公共 D 数据 { 获取;私人套装; }
  • 我想引用 Eric Lippert(C# 的权威,因为我不是我自己):“更具体地说,我会说你在滥用泛型。事实上,你已经类型关系太复杂以至于你无法分析自己,这表明你应该简化整个事情;如果你没有保持所有类型关系直截了当并且你写了东西,那么你的用户肯定无法保持直截了当任何一个。” stackoverflow.com/questions/17434851/…
  • 也许埃里克是对的,但这能回答我的问题吗?

标签: c# generics inheritance


【解决方案1】:

简化问题;

interface IObject { }
interface IUnit : IObject { }
interface IFoo : IUnit { }

abstract class Object<O, I>
    where O : Object<O, I>, I, new()
    where I : IObject
{}

class Unit : Object<Unit, IUnit>, IUnit
{
}

这很高兴并且可以编译。我在这里用IUnit 替换了I。现在换成更衍生的东西:

class Unit : Object<Unit, IFoo>, IUnit

你得到错误:

类型“Unit”不能用作泛型中的类型参数“O” 类型或方法“对象”。没有隐式引用 从“单位”转换为“IFoo”。

所以... Unit,派生自 IUnit 不能转换为 IFoo,即使两者都实现 IUnit... 因为 Unit 不派生自 IFoo...是对象的一个​​条件:

where O : Object<O, I>, I`

这要求你做一些你不被允许做的事情:

class Unit<I> : Object<Unit<I>, I>, I

【讨论】:

  • 老实说,我无法理解您帖子的第二部分。而且我看不出用接口替换实际类对我有什么帮助,顺便说一句。
  • 伙计们,您正试图向我展示如何简化模型。但遗憾的是,这不是我首先提出的问题。
  • 它的简化导致相同的错误(我在 Visual Studio 2015 上,措辞似乎不同)。基本上我相信你的问题是编译器无法转换Unit&lt;D, I&gt; to I,因为条件要求Unit&lt;D, I&gt; I,上面用更简单的术语表明了这一点。
  • 但是为什么呢? Unit 正在实现 IUnit,而“I”(遵循上述约束)是 IUnit 或从 IUnit 派生的东西。不是吗?
  • 我猜是因为它不能做协方差。如果您将 I 修复为 IUnit,它会变得很高兴。 class Unit&lt;D, I&gt; : Object&lt;D, Unit&lt;D, I&gt;, IUnit&gt;, IUnit
【解决方案2】:

就像其他人评论的那样,您的泛型太复杂了。 至少,在我看来,不需要I 类型参数,因为您的Objects 将实现相应的接口。

所以,代码可以这样简化:

abstract class Object<D, O> : IObject
    where D : ObjectData
    where O : Object<D, O>
{
}

class Unit<D> : Object<D, Unit<D>>, IUnit
    where D : UnitData
{
}

在没有完全理解的情况下,OObjects层次结构内部是如何使用的,很难说,是不是可以扔掉O类型参数。

您提到了静态工厂方法 - 这绝对不是带来如此复杂性的原因。但是,当然,您更了解用例。

【讨论】:

  • "I" 被定义为在 Object 上实现 IEquatable 和 IComparable。还有一个嵌套在实现 IEnumerable 的 Object 中的 Collection 类。是的,我知道这个层次结构太复杂了。我已经在标题中指定了这一点。我想了解的是错误的原因-对我来说这看起来不合理。不过还是谢谢你的回答。也许我真的需要简化所有这些。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-30
  • 2011-08-19
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多