通用类型的集合

时间:2010-07-09 18:14:32

标签: c# .net generics

如果我有一个通用类:

public class MyClass<T> 
{
  public T Value;
}

我想实例化几个项目,例如......

new MyClass<string>
new MyClass<int>

...并将它们添加到集合中。如何定义集合以便它可以包含泛型类型列表?然后我想在某个时刻迭代集合,并使用Value属性。可能?

10 个答案:

答案 0 :(得分:29)

让您的泛型类继承非泛型基类,或实现非泛型接口。然后,您可以拥有此类型的集合,并在您用于访问集合内容的任何代码中进行强制转换。

这是一个例子。

public abstract class MyClass
{
    public abstract Type Type { get; }
}

public class MyClass<T> : MyClass
{
    public override Type Type
    {
        get { return typeof(T); }
    }

    public T Value { get; set; }
}

// VERY basic illustration of how you might construct a collection
// of MyClass<T> objects.
public class MyClassCollection
{
    private Dictionary<Type, MyClass> _dictionary;

    public MyClassCollection()
    {
        _dictionary = new Dictionary<Type, MyClass>();
    }

    public void Put<T>(MyClass<T> item)
    {
        _dictionary[typeof(T)] = item;
    }

    public MyClass<T> Get<T>()
    {
        return _dictionary[typeof(T)] as MyClass<T>;
    }
}

答案 1 :(得分:8)

我能想到的唯一方法,如下所示(包含在控制台应用程序中进行测试):

class Program
{
    static void Main(string[] args)
    {
        var x = new MyClass<string>() { Value = "34" };
        var y = new MyClass<int>() { Value = 3 };

        var list = new List<IMyClass>();
        list.Add(x);
        list.Add(y);

        foreach (var item in list)
        {
            Console.WriteLine(item.GetValue);
        }
    }

    private interface IMyClass
    {
        object GetValue { get; }
    }

    private class MyClass<T> : IMyClass
    {
        public T Value;

        public object GetValue
        {
            get
            {
               return Value;
            }
        }
    }
}

即。让MyClass实现一个空接口,然后创建一个包含实现该接口的类实例的集合。

<强>更新 我在接口中添加了一个“GetValue”方法,允许您以对象的形式访问MyClass实例的“Value”。如果你想拥有一个包含混合类型的集合,这就像它将要获得的那样好。

答案 2 :(得分:3)

您需要为MyClass定义基类,然后您的集合将是基类列表。例如:

void Main()
{
 var a = new MyClass<string>();
 var b = new MyClass<int>();
 var c = new List<MyBase>();
 c.Add(a);
 c.Add(b);

}

public class MyBase { }
// Define other methods and classes here
public class MyClass<T> : MyBase {
public T Value { get; set;}
}

答案 3 :(得分:3)

您希望拥有一个MyClass集合,其中每个实例中类型参数T的值都不同。这在.NET中是不可能的;它缺少Java通配符(?)的等价物。您需要做的是创建一个非通用的基类或接口,MyClass可以实现它。例如:

public interface IMyClass {
  object Value { get; set; }
}
public class MyClass<T> : IMyClass {
  public T Value { get; set; }
  object IMyClass.Value {
    get { return Value; }
    set { Value = (T)value; }
  }
}
List<IMyClass> m = new List<IMyClass> { new MyClass<string>(), new MyClass<int> };

答案 4 :(得分:3)

我的大多数泛型类型都有“Untyped”成员接口:

private interface IMyClass
{
    object UntypedValue { get; }
}

private class MyClass<T> : IMyClass
{
    T Value { get; set; }

    object UntypedValue { get { return Value; } }
}

您也可以通过使用显式接口实现来完成此操作,但在我看来,使用单独的名称会更容易。 (关于显式接口实现也有一些CA提示)

答案 5 :(得分:2)

我认为这里的问题是泛型类实际上根本不是同一类型。它们只是在编译时创建全新类型的模板(如果我理解正确的话)。因此,根据运行时,MyClass<int>MyClass<string> 完全不同的类型。他们可能也是MyIntClassMyStringClass,你显然不能在同一个列表中没有先装箱。它们(必然)不继承相同的基类,实现相同的接口或其他任何东西。它们和其他两种类型一样不同,你必须这样对待它们(即使你认为你知道的更好)。

当然,您可以让它们实现接口,继承基础对象或已经给出的任何其他选项。请查看commongenius' answer以获得实现此目的的好方法。

答案 6 :(得分:2)

自.Net 3以来,有一个CompositeCollection类允许包含多个唯一项目甚至集合。 WPF开发人员使用它来存储和显示Xaml中的不同项。但这并不意味着它无法在非WPF情况下使用。

这是一个例子,我将不同的东西从字符串存储到小数,并提取和枚举所有项目,然后是特定类型的项目:

CompositeCollection cc = new CompositeCollection();

cc.Add(1);
cc.Add("Item One");
cc.Add(1.0M);
cc.Add(1.0);
cc.Add("Done");

Console.WriteLine ("Every Item");

foreach (var element in cc)
    Console.WriteLine ("\t" + element.ToString());

Console.WriteLine (Environment.NewLine + "Just Strings");

foreach (var str  in cc.OfType<string>())
    Console.WriteLine ("\t" + str);

/* Outputs:

Every Item
  1
  Item One
  1.0
  1
  Done

Just Strings
  Item One
  Done

*/

答案 7 :(得分:1)

我相信你的集合都必须是相同的MyClass类型(因为T必须是相同的),因为编译器不会知道你添加到集合中哪些元素的类型。 / p>

换句话说,如果你要在列表中添加2个元素:

list.Add(new MyClass<string>());
list.Add(new MyClass<int>());

然后尝试引用一个:

var myItem = list[1];

编译器不知道在元素1处为MyClass列表分配了什么泛型,因为元素是在运行时添加的,但泛型是在编译时确定的。

我很确定你想要做的事情无法完成。


如果您事先知道元素的数量,也许您可​​以使用Tuple

答案 8 :(得分:1)

的IList

无法定义可以接受任何通用类风格的通用集合......即IList<MyClass>。通用类只是开发人员在编写一堆重复代码时节省的捷径,但在编译时,泛型类的每种风格都被转换为具体的。即如果您有MyClass<string>MyClass<int>MyClass<bool>,则编译器将生成3个单独且不同的类。做你想做的事的唯一方法是为你的泛型提供一个接口。

public interface IMyGeneric {
    Type MyType { get; set;}    
}

class MyGeneric<T> : IMyGeneric {

    public MyGeneric() {
        MyType = typeof(T);
    }

    public Type MyType {
        get; set;
    }
}

然后你可以说

IList<IMyGeneric> list = new List<IMyGeneric>();
list.add(new MyGeneric<string>());
list.add(new MyGeneric<int>());

答案 9 :(得分:0)

另一种方法是使用非泛型类,并将处理程序传递给它以“从内部”执行操作。

interface IMyClass
{
    void ShowValues(MyHandler handler);
}

// Contains a List<string>.
class MyClassString : IMyClass
{
    private List<string> _list = new List<string> { "hello", "world" };

    public void ShowValues(MyHandler handler)
    {
        handler.ShowValues(_list);

    }
}

// Contains a List<int>.
class MyClassInt : IMyClass
{
    private List<int> _list = new List<int> { 1, 2 };

    public void ShowValues(MyHandler handler)
    {
        handler.ShowValues(_list);
    }
}

// Handler that has overloaded methods for all the supported types.
class MyHandler
{
    public void ShowValues(IEnumerable<string> list)
    {
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
    }

    public void ShowValues(IEnumerable<int> list)
    {
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var myClasses = new IMyClass[]
        {
            new MyClassString(),
            new MyClassInt(),
        };

        var myHandler = new MyHandler();

        foreach (var myClass in myClasses)
        {
            myClass.ShowValues(myHandler);
        }
    }
}