【问题标题】:Collection of generic types泛型类型的集合
【发布时间】:2010-07-09 18:14:31
【问题描述】:

如果我有一个泛型类:

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

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

new MyClass<string>
new MyClass<int>

...并将它们添加到集合中。如何定义集合以便它可以保存泛型类型列表?然后我想在某个时候遍历集合,并使用 Value 属性。可能的?

【问题讨论】:

  • 你是如何声明你的班级的?您是如何定义支持集合的?
  • 这只是“假设”,还是您尝试将其用作解决方案的一部分?我问是因为如果有更多的上下文,我们可能会提出一个更好的答案.. :)
  • 后来又转发了几乎相同的问题(我投票决定关闭,但可能不会成功,因为它太旧了):stackoverflow.com/q/3777057/590790

标签: c# .net generics


【解决方案1】:

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

这是一个例子。

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

【讨论】:

  • 返回类型是一个有趣的变化。
  • 您的解决方案绝对是优雅的。保留类型的内部字典确实使访问方法更简单。
  • 此外,您现在可以使用dynamic 关键字:msdn.microsoft.com/en-us/library/dd264736.aspx
  • 在这种情况下,不能添加相同 T 的多个项目。例如。该集合不能包含两个MyClass&lt;string&gt;
【解决方案2】:

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

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”作为对象访问。如果您想拥有一个包含混合类型的集合,这将是最好的,afaik。

【讨论】:

  • 好电话 - 这是我使用基类的等效答案:)
  • 但是如何访问 value 属性?
  • 在这种情况下,您可以从 List 开始并称之为一天......(从头开始,我重新阅读并意识到该界面至少为您提供了值属性)。
  • @Jeremy - 我更新了我的答案,在接口中包含一个“GetValue”方法,该方法将 MyClass 的“Value”属性作为对象返回。据我所知,这是最好的,至少在 3.5 及以下版本中。在 4.0 中,您可能可以使用 Co/Contra-Variance 做一些时髦的事情,但我还没有探索过 :)
  • 但是,正如 rally25s 所建议的,由于对存储数据的唯一访问是通过 Object,那么您不妨将其存储为 Object,并将其设为非泛型类。跨度>
【解决方案3】:

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

private interface IMyClass
{
    object UntypedValue { get; }
}

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

    object UntypedValue { get { return Value; } }
}

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

【讨论】:

    【解决方案4】:

    您需要为 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;}
    }
    

    【讨论】:

    • 在您的示例中,可能值得将 MyBase 标记为抽象类,以便更清楚地说明意图是什么,.. :)
    • 有趣...如何遍历列表并检查 Value 属性的内容?
    • 您需要遍历集合,并在其中调用 GetType().GetGenericParameters().First() 以获取类型,然后将 Value 作为对象提取并将其转换为提取的前一个类型...工作量很大,因此您可能需要考虑此逻辑的目的,看看是否没有不同的方法来完成相同的结果
    【解决方案5】:

    您希望拥有一个 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> };
    

    【讨论】:

    • @commongenius,你确认它不会编译吗?它在 VS2k8 中为我编译得很好
    • 当我发表我的评论时,原来的帖子已经改变了。我相应地更改了我的评论。
    【解决方案6】:

    我认为这里的问题是泛型类实际上根本不是同一种类型。它们只是在编译时创建全新类型的模板(如果我理解正确的话)。因此,根据运行时,MyClass&lt;int&gt;MyClass&lt;string&gt;完全不同的类型。它们也可能是MyIntClassMyStringClass,如果不先将它们装箱,您显然无法将它们放在同一个列表中。它们(不一定)继承相同的基类、实现相同的接口或其他任何东西。它们与外面的任何其他两种类型一样不同,您必须这样对待它们(即使您认为自己更了解)。

    当然,您可以让它们实现接口、继承基础对象或任何其他已给出的选项。请查看commongenius' answer 了解如何执行此操作。

    【讨论】:

      【解决方案7】:

      自 .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
      
      */
      

      【讨论】:

        【解决方案8】:

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

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

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

        然后尝试引用一个:

        var myItem = list[1];
        

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

        我很确定你想做的事情做不到。


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

        【讨论】:

          【解决方案9】:

          列表

          没有办法定义一个可以接受任何风格的泛型类的泛型集合...即IList&lt;MyClass&gt;。泛型类只是开发人员节省编写一堆重复代码的捷径,但在编译时,泛型类的每种风格都被翻译成具体的。即如果您有MyClass&lt;string&gt;MyClass&lt;int&gt;MyClass&lt;bool&gt;,那么编译器将生成 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>());
          

          【讨论】:

            【解决方案10】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-06-11
              • 2017-12-24
              • 2011-04-16
              • 1970-01-01
              • 1970-01-01
              • 2021-12-04
              相关资源
              最近更新 更多