C#中的泛型类型:将类型参数限制为集合

时间:2009-10-29 10:32:27

标签: c# generics collections

我需要编写一个泛型类,其中type参数必须是实现ICollection<T>的东西。在MyClass内,我需要收藏品的项目类型(在标有???的代码段中)。

class MyClass<TCollection> where TCollection : ICollection<???>
{
  // ...

  public void store(??? obj) { /* put obj into collection */ }

  // ...
}

该集合通常实际上是一本字典。有时,它会像列表一样简单。

我确切知道如何在C ++中执行此操作。我怎么做这个是C#?

5 个答案:

答案 0 :(得分:5)

嗯,您通常使用其他类型参数执行此操作:

class MyClass<TCollection, TElement> where TCollection : ICollection<TElement>
{
  // ...

  public void store(TElement obj) { }

  // ...
}

在这种情况下你真的需要TCollection吗?你能用TElement吗?

答案 1 :(得分:3)

最简单的要做的事情就是只指定元素类型,并在任何需要的地方硬编码ICollection<T>,例如。

class MyClass<T> {

    private ICollection<T> _items;

    public MyClass(ICollection<T> items) {
        _items = items;
    }

    public void Store(T obj) {
        _items.Add(obj);
    }

    public ICollection<T> Items {
        get {
            return _items;
        }
    }
}

我建议您将集合实例传递给构造函数,而不是在内部创建一个。它使类更简单,更“通用”(借口双关语),并允许您使用非默认构造函数构造集合实例,例如带有非默认比较器的字典。

重新编辑(第三次尝试):使用类继承和命名空间别名来模拟typedef都可以达到一定程度,但在某些情况下两个抽象都会中断。这段代码是我发现的最简单的实际编译代码。

第1步 - 定义这些类:

// This KeyValuePair was being used to simulate a tuple. We don't need to simulate a tuple when we have a concrete class.
class BazAndListOfWrgl {
    Baz Baz { get; set; }
    List<Wrgl> Wrgls { get; set; }
}

// Simple typedef.
class BazAndListOfWrglDictionary : Dictionary<Bar, BazAndListOfWrgl> { }

第2步 - 定义这些命名空间别名。所有标识符必须完全合格。引用的所有类型必须已在另一个物理文件中定义 (因为名称空间别名必须在文件中的所有代码之前)。

using OuterDictionary = System.Collections.Generic.Dictionary<MyNamespace.Foo, MyNamespace.BazAndListOfWrglDictionary>;
using OuterDictionaryItem = System.Collections.Generic.KeyValuePair<MyNamespace.Foo, MyNamespace.BazAndListOfWrglDictionary>;

第3步 - 像这样使用它们:

class Program {

    static void Main() {

        // List-based example.
        var listWrapper = new MyClass<BazAndListOfWrgl>(new List<BazAndListOfWrgl>());
        listWrapper.Store(new BazAndListOfWrgl());
        Console.WriteLine(listWrapper.Items.Count);

        // Dictionary-based example.
        var dictionaryWrapper = new MyClass<OuterDictionaryItem>(new OuterDictionary());
        dictionaryWrapper.Store(new OuterDictionaryItem(new Foo(), new BazAndListOfWrglDictionary()));
        Console.WriteLine(dictionaryWrapper.Items.Count);

    }
}

推理是:BazAndListOfWrglDictionary不能是命名空间别名,因为命名空间别名不能相互依赖。 OuterDictionaryOuterDictionaryItem不能是派生类,否则编译器不会将其识别为另一个的元素。

答案 2 :(得分:2)

这对我有用:

class MyClass<TCollection,T> where TCollection : ICollection<T> , new()
{
    private TCollection collection;           

    public MyClass()
    {
        collection = new TCollection();
    }

    public void Store(T obj)
    {
        collection.Add(obj);
    }

    public TCollection Items
    {
        get { return collection; }
    }
}

用法:

MyClass<List<string>, string> myclass = new MyClass<List<string>, string>();
myclass.Store("First element");
myclass.Store("Second element");
myclass.Items.ForEach(s => Console.WriteLine(s));

编辑: 当T变得越来越复杂时,你可能想看一下using directive,你可以(误)将它用作typedef。

using HugeType = System.Collections.Generic.Dictionary<int, string>; // Example 'typedef'
...

//Create class, note the 'typedef' HugeType
MyClass<List<HugeType>, HugeType> myclass = new MyClass<List<HugeType>, HugeType>();
//Fill it
HugeType hugeType1 = new HugeType();
hugeType1.Add(1, "First");
hugeType1.Add(2, "Second");
HugeType hugeType2 = new HugeType();
hugeType1.Add(3, "Third");
hugeType1.Add(4, "Fourth");
myclass.Store(hugeType1);
myclass.Store(hugeType2);
//Show it's values.
myclass.Items.ForEach(element => element.Values.ToList().ForEach(val => Console.WriteLine(val)));

答案 3 :(得分:1)

你不能这样做吗?:

class MyClass<T, TCollection>  where T: YourTypeOrInterface
                               where TCollection : ICollection<T>
{
    public void store(T obj) { }
}

并且,没有经过测试,但我能想到的另一种方法是在存储项目时指定类型:

class MyClass<TCollection> where TCollection : System.Collections.ICollection
{

    TCollection Collection;

    public void Store<T>(T obj) 
    {
        ((ICollection<T>)this.Collection).Add(obj);
    }

}

答案 4 :(得分:1)

是否有公开成员公开类型TCollection

如果有任何此类成员,您是否获得了可以公开TCollection的直接类型ICollection<T>的任何价值?

您的客户应该实现ICollection<T>接口,因此它不会真正影响您的对象;你只会打电话给那个界面,而不是他们的特定类型。因此,您的对象只需要知道存储在集合中的元素的类型。

在这个前提下,课程可以这样写:

public class MyClass<T>
{
    public MyClass(ICollection<T> collection) 
    { 
        /* store reference to collection */ 
    }

    // ...

    public void store(T obj) 
    { 
        /* put obj into collection */ 
    }

    // ...
}