C#:声明和使用不同类型的泛型类列表,如何?

时间:2009-02-26 13:27:40

标签: c# .net generics list

让以下通用类包含 string, int, float, long 作为类型:

public class MyData<T>
{
    private T _data;

    public MyData (T value)
    {
        _data = value;
    }

    public T Data { get { return _data; } }
}

我正在尝试获取 MyData<T> 的列表,其中每个项目的 T 不同。

我希望能够从列表中访问项目并获取其值,如下面的代码所示:

MyData<> myData = _myList[0];    // Could be <string>, <int>, ...
SomeMethod (myData.Data);

其中 SomeMethod() 声明如下:

public void SomeMethod (string value);
public void SomeMethod (int value);
public void SomeMethod (float value);

更新

SomeMethod() 来自我无法控制的其他等级类,并且 SomeMethod(object) 不存在。


但是,我似乎找不到让编译器满意的方法。

有什么建议吗?

谢谢。

9 个答案:

答案 0 :(得分:13)

我认为您遇到的问题是因为您尝试创建泛型类型,然后创建该泛型类型的列表。您可以通过外包您尝试支持的数据类型(例如IData元素)来完成您尝试执行的操作,然后使用IData约束创建MyData泛型。这样做的缺点是你必须创建自己的数据类型来表示你正在使用的所有原始数据类型(string,int,float,long)。它可能看起来像这样:

public class MyData<T, C>
    where T : IData<C>
{
    public T Data { get; private set; }

    public MyData (T value)
    {
         Data = value;
    }
}

public interface IData<T>
{
    T Data { get; set; }
    void SomeMethod();
}

//you'll need one of these for each data type you wish to support
public class MyString: IData<string>
{
   public MyString(String value)
   {
       Data = value;
   }

   public void SomeMethod()
   {
       //code here that uses _data...
       Console.WriteLine(Data);
   }

   public string Data { get; set; }
}

然后你的实现将是这样的:

var myData = new MyData<MyString, string>(new MyString("new string"));    
// Could be MyString, MyInt, ...
myData.Data.SomeMethod();

这是一个更多的工作,但你得到了你想要的功能。

<强>更新 从您的界面中删除SomeMethod并执行此操作

SomeMethod(myData.Data.Data);

答案 1 :(得分:8)

代表们可以真正帮助简化这一过程,并且仍然保证类型安全:

public void TestMethod1()
{
    Action<SomeClass, int> intInvoke = (o, data) => o.SomeMethod(data);
    Action<SomeClass, string> stringInvoke = (o, data) => o.SomeMethod(data);

    var list = new List<MyData> 
    {
        new MyData<int> { Data = 10, OnTypedInvoke = intInvoke },
        new MyData<string> { Data = "abc", OnTypedInvoke = stringInvoke }
    };

    var someClass = new SomeClass();
    foreach (var item in list)
    {
        item.OnInvoke(someClass);
    }
}

public abstract class MyData
{
    public Action<SomeClass> OnInvoke;
}

public class MyData<T> : MyData
{
    public T Data { get; set; }
    public Action<SomeClass, T> OnTypedInvoke 
    { set { OnInvoke = (o) => { value(o, Data); }; } }
}

public class SomeClass
{
    public void SomeMethod(string data)
    {
        Console.WriteLine("string: {0}", data);
    }

    public void SomeMethod(int data)
    {
        Console.WriteLine("int: {0}", data);
    }
}

答案 2 :(得分:6)

只需使用ArrayList并忘记MyData<T>类型。

ArrayList myStuff = getStuff();
float x = myStuff.OfType<float>().First();
SomeMethod(x);
string s = myStuff.OfType<string>().First();
SomeMethod(s);

MyData<T>的问题在于您希望编译器检查仅在运行时已知的类型。编译器检查编译时已知的类型。

答案 3 :(得分:3)

你不能按照你想要的方式去做。

当初始化泛型类的实例时,它将绑定到特定类型。由于您希望在列表中保存不同类型的对象,因此必须创建一个绑定到最小公分母的实例 - 在您的情况下它是对象。

但是,这意味着Data属性现在将返回Object类型的对象。编译器无法在编译时推断出实际的数据类型,因此可以选择适当的SomeMethod重载。

您必须提供SomeMethod的重载,将Object作为参数,或者删除在集合中保存不同类型的不同类型的要求。

或者您可以使用标准IEnumerable集合(如Array)并使用OfType<>扩展方法获取特定类型集合的子集。

答案 4 :(得分:1)

在这种情况下,您需要MyData<object>,因为这是这些类型的唯一共同点。

答案 5 :(得分:1)

一段时间后推荐的通配符here。关闭为“不会修复”:(

答案 6 :(得分:1)

您可以为SomeMethod创建一个通用包装器并检查泛型参数的类型,然后委托给相应的方法。

public void SomeMethod<T>(T value)
{
    Type type = typeof(T);

    if (type == typeof(int))
    {
        SomeMethod((int) (object) value); // sadly we must box it...
    }
    else if (type == typeof(float))
    {
        SomeMethod((float) (object) value);
    }
    else if (type == typeof(string))
    {
        SomeMethod((string) (object) value);
    }
    else
    {
        throw new NotSupportedException(
            "SomeMethod is not supported for objects of type " + type);
    }
}

答案 7 :(得分:0)

从非通用MyData<T>类继承MyData并列出该列表。

这样,您就无法自动解决重载问题。你必须手动完成。

abstract class MyData { 
   protected abstract object GetData();
   protected abstract Type GetDataType(); 
   public object Data {
      get { return GetData(); } 
   }
   public Type DataType {
      get { return GetDataType(); } 
   }
}

class MyData<T> : MyData { 
   protected override object GetData() { return Data; }
   protected override Type GetDataType() { return typeof(T); }
   public new T Data {
     get { ... }
   }
}

答案 8 :(得分:0)

泛型允许您在创建列表时为整个列表指定一种类型,例如,存储int的列表将像这样创建

var myData = new MyData<int>();

如果要在同一通用列表中存储多个类型,可以为这些类型指定公共基本类型或接口。不幸的是,在您的情况下,您要存储的类型的唯一公共基类型将是object。

var myData = new MyData<object>();

但您可以使用非通用列表来存储对象。