【问题标题】:C#: Declaring and using a list of generic classes with different types, how?C#:声明和使用具有不同类型的泛型类列表,如何?
【发布时间】:2010-10-10 02:02:42
【问题描述】:

具有以下泛型类,该类将包含 string, int, float, long 作为类型:

public class MyData<T>
{
    private T _data;

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

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

我正在尝试获取 MyData&lt;T&gt; 的列表,其中每个项目将是不同的 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) 不存在。


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

有什么建议吗?

谢谢。

【问题讨论】:

  • 您能否详细说明您的 SomeMethod 的作用?我们或许能更好地帮助您。
  • SomeMethod 所做的与这里无关。我只是补充一点,SomeMethod(object) 没有定义。
  • 您的基本设计有缺陷。我建议您提出一个问题,详细说明您的要求和解决方案并征求建议。

标签: c# .net generics list


【解决方案1】:

我认为您遇到的问题是因为您尝试创建一个泛型类型,然后创建该泛型类型的列表。您可以通过外包您尝试支持的数据类型(例如 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);

【讨论】:

  • 太糟糕了,我需要包装原始类型,但这是我愿意接受的,因为它看起来没有其他方式。但是,在您的示例中,您假设 SomeMethod() 是 MyData 的一部分,但事实并非如此(我已经更新了问题)。
  • 其实你可以完成同样的事情,我只是把它包装起来,这样它会更加封装,看我的更新
  • 那么...如何将 MyData 和 MyData 放在同一个集合中?
  • 你必须创建另一个接口,比如 IDataType 并重新定义 IData 接口来说 IData where T : IDataType,然后每个具体类型也必须实现 IDataType,比如MyString 类:IData,IDataType
  • 那么你可以说像这样实例化一个列表: new List();并传入 MyData 和 MyData 只要两者都是 IDataType 类型。
【解决方案2】:

委托确实可以帮助简化这一点,并且仍然保持类型安全:

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);
    }
}

【讨论】:

  • 令人大开眼界。
【解决方案3】:

只需使用 ArrayList 并忘记 MyData&lt;T&gt; 类型。

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

MyData&lt;T&gt; 的问题在于您希望编译器检查仅在运行时才知道的类型。编译器检查编译时已知的类型。

【讨论】:

  • 谢谢!这是我个人问题的答案。试图跟踪 BlockingCollection 的列表,然后通过 GetQueue where T : Interface 方法检查我的列表中是否已经存在类型 T 的队列,如果不存在,则实例化并返回新的空队列。但是 BlockingCollection 不能隐式转换为 BlockingCollection,甚至认为 T : Interface,我终其一生都找不到正确的协变/逆变/委托实现来执行此操作
【解决方案4】:

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

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

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

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

或者您可以使用标准的IEnumerable 集合(如数组)并使用OfType&lt;&gt; 扩展方法来获取特定类型的集合的子集。

【讨论】:

  • 我担心它会解决使用对象...但是,我不能使用对象,因为 SomeMethod 来自库,我无法改变它不会接受的事实一个对象...另外,我不想 switch() 变量的类型...
【解决方案5】:

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

【讨论】:

  • MyData 如何允许调用 SomeMethod(int) ?
【解决方案6】:

您可以为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】:

不久前推荐的通配符here。关闭为“不会修复”:(

【讨论】:

    【解决方案8】:

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

    var myData = new MyData<int>();
    

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

    var myData = new MyData<object>();
    

    但您可以只使用非泛型列表来存储对象。

    【讨论】:

      【解决方案9】:

      从非泛型 MyData 类继承 MyData&lt;T&gt; 并列出它。

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

      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 { ... }
         }
      }
      

      【讨论】:

      • 好的,现在我会像这样使用它: List list = new List(); list.Add (new MyData()); list.Add (new MyData()); SomeMethod (list[0].Data);不知何故,我需要让 SomeMethod() 接受一个对象。不幸的是,这不是我需要的。感谢回复。斯泰西
      • 是的,这就是为什么我说您应该通过考虑类型来手动管理重载。泛型不支持直接的方式。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-04
      • 2021-07-05
      • 2019-11-21
      • 1970-01-01
      相关资源
      最近更新 更多