使用IEnumerable <t> </t>创建通用集合的通用类

时间:2012-02-10 22:09:54

标签: c# oop generics

这是关于泛型的两(2)部分问题

我必须创建几个类似的类来为类似设计的数据库表建模。

所有表格都包含ID intText nvarchar(50)字段。一两个可能包含一些其他字段。

我很少使用generics,但我经常在这里看到它的例子。这是我创建另一个泛型类中使用的泛型类的最大尝试。

我的基本结构如下,我将用注释指出什么不起作用以及Visual Studio 2010显示的错误消息:

public class IdText {

  public IdText(int id, string text) {
    ID = id;
    Text = text;
  }

  public int ID { get; private set; }

  public string Text { get; private set; }

}

public class TCollection<T> : IEnumerable<T> where T : IdText {

  private List<T> list;

  public TCollection() {
    list = new List<T>();
  }

  public void Add(int id, string text) {
    foreach (var item in list) {
      if (item.ID == id) {
        return;
      }
    }
    list.Add(new T(id, text)); // STOP HERE
    // Cannot create an instance of the variable type 'T'
    // because it does not have the new() constraint
  }

  public T this[int index] {
    get {
      if ((-1 < 0) && (index < list.Count)) {
        return list[index];
      }
      return null;
    }
  }

  public T Pull(int id) {
    foreach (var item in list) {
      if (item.ID == id) {
        return item;
      }
    }
    return null;
  }

  public T Pull(string status) {
    foreach (var item in list) {
      if (item.Text == status) {
        return item;
      }
    }
    return null;
  }

  #region IEnumerable<T> Members

  public IEnumerator<T> GetEnumerator() {
    foreach (var item in list) yield return item;
  }

  #endregion

  #region IEnumerable Members

  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
    return list.GetEnumerator();
  }

  #endregion

}

Visual Studio的 IntelliSence 希望我添加list.Add(T item),但我需要先创建它。

我试图将有问题的行list.Add(new T(id, text));重新编写为list.Add(new IdText(id, text));,但后来我被谴责,消息“无法从IdText转换为T }“

我究竟如何解决这个问题?

下一步:当我稍后进入实际创建此IdText课程的版本时,我不确定我在TCollection中如何使用这个新课程我设计的课程。

例如,给定此派生类:

public class ManufacturedPart : IdText {

  public ManufacturedPart(int id, string partNum, string description)
    : base(int id, string partNum) {
    Description = description;
  }

  public string Description { get; private set; }

}

...我还需要派一个特殊版本的TCollection来陪伴吗?

public class ManufacturedParts<T> : IEnumerable<T> where T : ManufacturedPart {

  // OK, now I'm lost! Surely this can't be right!

}

3 个答案:

答案 0 :(得分:5)

1)您可以使用new()约束,公开您的属性并添加无参数构造函数:

public class IdText
{
    public IdText()
    {
    }

    public IdText(int id, string text)
    {
        ID = id;
        Text = text;
    }

    public int ID { get; set; }

    public string Text { get; set; }

}

public class TCollection<T> : IEnumerable<T> where T : IdText, new()
{

    private List<T> list;

    public TCollection()
    {
        list = new List<T>();
    }

    public void Add(int id, string text)
    {
        foreach (var item in list)
        {
            if (item.ID == id)
            {
                return;
            }
        }
        list.Add(new T { ID = id, Text = text }); 
    }
}

2)您有多种选择:

如果您希望自己的收藏集存储任何IdTextManufacturedPart或其他来自IdText的内容):

TCollection<IdText> ss = new TCollection<IdText>();

以上,目前只能在IdText方法中实例化对象时存储Add(int, string),但如果您提供Add(T object)方法,它可以存储任何IdText实例。

如果您希望您的收藏只包含ManufacturedParts

public class ManufacturedParts<T> : TCollection<T> where T : ManufacturedPart, new()
{
     // Provide here some specific implementation related to ManufacturedParts
     // if you want. For example, a TotalPrice property if ManufacturedPart
     // has a Price property.
}

TCollection<ManufacturedPart> ss2 = new ManufacturedParts<ManufacturedPart>();

甚至更简单,如果您的集合没有提供任何其他方法,具体取决于存储对象的类型:

TCollection<ManufacturedPart> ss2 = new TCollection<ManufacturedPart>();

更简单的是,如果您的目标只是存储对象,则不需要自定义集合:

List<IdText> ss2 = new List<IdText>();  // Uses the built-in generic List<T> type

答案 1 :(得分:1)

关于第一个问题:c#不支持将参数作为通用约束的构造函数。你可以做的就是用

替换它
(T)Activator.CreateInstance(typeof(T),new object[]{id,text});

另一方面......你实际上并不知道派生类的构造函数是什么样的,所以你不能确保它们会有那个构造函数。

关于第二个问题,你可以这样做:

var collection = new TCollection<ManufacturedPart>();

以与List相同的方式工作。

希望它有所帮助。

答案 2 :(得分:1)

如果您的集合类负责实例化其收集类型的元素,那么您可能不希望使用new()约束或Activator.CreateInstance() - - Jon Skeet has blogged,这两个都表现不佳。

听起来您真正想要的是提供商委托,如下所示:

public class MyCollection<T> : IEnumerable<T> where T : IdText {
    private readonly List<T> list;
    private readonly Func<int, string, T> provider;

    public MyCollection(Func<int, string, T> provider) {
        this.list = new List<T>();
        this.provider = provider;
    }

    public void Add(int id, string text) {
        list.Add(provider(id, text));
    }
}

然后你就像使用它一样:

var collection = new MyCollection((id, text) => new ManufacturedPart(id, text));

您可以将此视为将要用作参数的特定构造函数传递给类,然后根据需要使用它来构造实例。

并且您不需要为MyCollection<ManufacturedPart>创建单独的子类 - 只需直接使用泛型类。