【问题标题】:Create list of generics创建泛型列表
【发布时间】:2012-10-01 09:03:45
【问题描述】:

我的实体有基类

public class Entity<T> where T : Entity<T>, new()
{
    public XElement ToXElement()
    {
    }
    public static T FromXElement(XElement x)
    {
    }
}

我必须使用这种奇怪的构造Entity&lt;T&gt; where T : Entity&lt;T&gt;,因为我希望静态方法 FromXElement 是强类型的 另外,我有一些实体,像这样的

public class Category : Entity<Category>
{
}
public class Collection : Entity<Collection>
{
}

如何使用基类创建我的实体的通用列表?

var list = new List<Entity<?>>();
list.Add(new Category());
list.Add(new Collection());

【问题讨论】:

  • 你想完成什么?为什么需要集合中的这些不同类型?
  • 类 Category 和 Collection 是...本身的泛型类型?
  • @DanielPersson 它被称为curiously repeating template pattern
  • 好的,我明白了。谢谢你把它理顺了:)
  • 您不需要 Entity 约束,如果它的唯一目的是访问 FromXElement。只需从 Entity 派生(即 Category : Entity { }),您可以通过 Category.FromXElement(element) 调用它i> 这将具有 Category 的强类型返回值

标签: c# generics collections


【解决方案1】:

你不能用那个定义。 CategoryCollection 之间没有“公共基类”(当然,object 除外)。

如果有,说如果Entity&lt;T&gt; 被定义为:

public class Entity
{
}

public class Entity<T> : Entity where T : Entity<T>, new()
{
    public XElement ToXElement()
    {
    }
    public static T FromXElement(XElement x)
    {
    }
}

那你就可以了

var list = new List<Entity>();
list.Add(new Category());
list.Add(new Collection());

但这会给你带来什么?

【讨论】:

  • 我需要基类中的 FromXElement/ToXElement。这不合适
  • @Evgraf - 那么你将不得不投。 CategoryCollection 都没有实现 FromXElement 方法(因为它们具有不同的返回类型)。
【解决方案2】:

你可以定义一个非泛型类作为所有实体类的基类

public class Entity
{
}

并使Entity继承Entity

public class Entity<T> : Entity where T : Entity<T>, new()
{
}

现在您可以将实体列表创建为:

var list = new List<Entity>();

【讨论】:

    【解决方案3】:

    您可以通过添加类的非泛型版本来解决此问题

    class Entity
    {
      // methods
    
      public T As<T>() 
      { 
        if (this is T) return (T)this;
        throw new InvalidCastException();
      }
    }
    
    class Entity<T> : Entity where T : Entity<T>, new()
    
    class Cathegory : Entity<T> {}
    

    然后创建基类的列表:

    var list = new List<Entity>()
    list.Add(new Cathegory());
    

    然后,如果要调用“通用特定”操作,则需要调用“As”函数或简单地转换对象。

    【讨论】:

      【解决方案4】:

      创建标记界面:

      public interface IAmAGenericEntity { }
      
      public class Entity<T> where T : IAmAGenericEntity, new()
      // ...
      
      public class Category : Entity<T>, IAmAGenericEntity
      // ....
      
      var list = new List<IAmAGenericEntity>();
      // ...
      

      【讨论】:

        【解决方案5】:

        由于Entity 上缺少abstract 标记,我假设To/FromXElement 使用反射并且应该适用于Entity 的任何子类型。我建议您按如下方式构建类:

        public class Entity
        {
            public XElement ToXElement() { ... }
        
            protected static T FromXElement<T>(XElement x)
                where T : Entity
            {
                ...
            }
        }
        
        public class Category : Entity
        {
            public static Category : FromXElement(XElement x)
            {
                return FromXElement<Category>(x);
            }
        }
        

        “样板”是最小的,它不需要您创造性地规避类型系统。您不必担心缺乏共同基础或手动转换。如果您愿意,您可以完全消除样板文件,直接从 Entity 构造您的对象:

        public class Entity
        {
            public XElement ToXElement() { ... }
        
            public static T FromXElement<T>(XElement x)
                where T : Entity
            {
                ...
            }
        }
        

        本质上,您所做的是实现一个 C# 不直接支持的类型类。有很多方法可以解决这个缺陷,但我通常发现它们比它们的价值更麻烦,尤其是在涉及静态方法时。如果 C# 支持静态扩展方法,那就简单了,可惜不支持。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-07-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多