【问题标题】:Can I Create a Dictionary of Generic Types?我可以创建泛型类型的字典吗?
【发布时间】:2010-10-13 21:00:43
【问题描述】:

我想创建一个带有字符串键的 Dictionary 对象,其中包含泛型类型的值。我想它看起来像这样:

Dictionary<string, List<T>> d = new Dictionary<string, List<T>>();  

并允许我添加以下内容:

d.Add("Numbers", new List<int>());
d.Add("Letters", new List<string>());

我知道我可以对字符串列表执行此操作,例如,使用以下语法:

Dictionary<string, List<string>> d = new Dictionary<string, List<string>>();
d.Add("Key", new List<string>());

但如果可能的话,我想为一个通用列表做这个......

那么 2 个问题:

  1. 有可能吗?
  2. 语法是什么?

【问题讨论】:

    标签: c# generics


    【解决方案1】:

    编辑:现在我重新阅读了这个问题......

    您不能这样做,但自定义集合会在一定程度上处理它。你基本上有一个通用的Add 方法:

    public void Add<T>(string key, List<T> list)
    

    (集合本身不会是通用的 - 除非您想将键类型设为通用。)

    您不能以强类型的方式从其中提取值,因为编译器不会知道您对特定键使用了哪种类型。如果您将键设置为类型本身,您会以稍微更好的情况结束,但现有集合仍然不支持这种情况。这就是我最初的答案所回应的情况。

    编辑:原始答案,当我没有完全正确阅读问题时,但无论如何这可能会提供信息......

    不,你不能让一种类型的参数依赖于另一种,恐怕。这只是人们可能希望在泛型类型系统中表达但.NET 的约束不允许的事情之一。总会有这样的问题,.NET 设计者选择保持泛型相对简单。

    但是,您可以编写一个集合来相当容易地执行它。我有an example in a blog post,它只保留一个值,但很容易扩展它以使用列表。

    【讨论】:

    • 啊,我现在看到了问题 - 无法对从集合中提取的任何内容进行类型推断。我想这就是为什么它不包含在功能中的原因。谢谢!
    • 集合本身也可以是通用的:使用C where T : C 会很有用。
    【解决方案2】:

    这样的东西有用吗?

    public class GenericDictionary
    {
        private Dictionary<string, object> _dict = new Dictionary<string, object>();
    
        public void Add<T>(string key, T value) where T : class
        {
            _dict.Add(key, value);
        }
    
        public T GetValue<T>(string key) where T : class
        {
            return _dict[key] as T;
        }
    }
    

    基本上,它为您包装了所有幕后演员。

    【讨论】:

    • 这就是解决方案。简单的。有效的。谢谢。 :)
    • 如果值既可以是引用类型又可以是值类型,则 GetValue() 中的强制转换不起作用..
    【解决方案3】:

    Dictionary&lt;string, dynamic&gt; 怎么样? (假设您使用的是 C# 4)

    Dictionary<string, dynamic> Dict = new Dictionary<string, dynamic>();
    

    来源:https://stackoverflow.com/a/5038029/3270733

    【讨论】:

      【解决方案4】:

      我更喜欢这种将泛型类型放入集合的方式:

      interface IList
      {
        void Add (object item);
      }
      
      class MyList<T> : List<T>, IList
      {
        public void Add (object item)
        {
          base.Add ((T) item); // could put a type check here
        }
      }
      
      class Program
      {
        static void Main (string [] args)
        {
          SortedDictionary<int, IList>
            dict = new SortedDictionary<int, IList> ();
      
          dict [0] = new MyList<int> ();
          dict [1] = new MyList<float> ();
      
          dict [0].Add (42);
          dict [1].Add ("Hello"); // Fails! Type cast exception.
        }
      }
      

      但您确实会在编译时丢失类型检查。

      【讨论】:

      • 正是我的想法:使用字典.
      【解决方案5】:

      我使用ConditionalWeakTable 来实现类型安全。

      public class FieldByType
      {
          static class Storage<T>
              where T : class
          {
              static readonly ConditionalWeakTable<FieldByType, T> table = new ConditionalWeakTable<FieldByType, T>();
      
              public static T GetValue(FieldByType fieldByType)
              {
                  table.TryGetValue(fieldByType, out var result);
                  return result;
              }
      
              public static void SetValue(FieldByType fieldByType, T value)
              {
                  table.Remove(fieldByType);
                  table.Add(fieldByType, value);
              }
          }
      
          public T GetValue<T>()
              where T : class
          {
              return Storage<T>.GetValue(this);
          }
      
          public void SetValue<T>(T value)
              where T : class
          {
              Storage<T>.SetValue(this, value);
          }
      }
      

      可以这样使用:

      /// <summary>
      /// This class can be used when cloning multiple related objects to store cloned/original object relationship.
      /// </summary>
      public class CloningContext
      {
          readonly FieldByType dictionaries = new FieldByType();
      
          public void RegisterClone<T>(T original, T clone)
          {
              var dictionary = dictionaries.GetValue<Dictionary<T, T>>();
              if (dictionary == null)
              {
                  dictionary = new Dictionary<T, T>();
                  dictionaries.SetValue(dictionary);
              }
              dictionary[original] = clone;
          }
      
          public bool TryGetClone<T>(T original, out T clone)
          {
              var dictionary = dictionaries.GetValue<Dictionary<T, T>>();
              if (dictionary == null)
              {
                  clone = default(T);
                  return false;
              }
      
              return dictionary.TryGetValue(original, out clone);
          }
      }
      

      另请参阅this question,其中值的类型作为键的通用参数存储在其中。

      【讨论】:

      • 好东西!但是你为什么用ConditionalWeakTable呢?
      • @AndrewVorobyov,那是旧的!这是一个ConditionalWeakTable,所以如果用户没有任何参考,FieldByType 的实例可以被垃圾收集。如果它是Dictionary,它将保留所有FieldByType 实例的引用,并且无法完成GC 收集。
      【解决方案6】:

      我们使用大量反射来创建可扩展的管理工具。我们需要一种在模块定义的全局搜索中注册项目的方法。每个搜索都会以一致的方式返回结果,但每个搜索都有不同的依赖关系。下面是我们为单个模块注册搜索的示例:

      public void ConfigureSearch(ISearchConfiguration config)
          {
              config.AddGlobalSearchCallback<IEmploymentDataContext>((query, ctx) =>
              {
                  return ctx.Positions.Where(p => p.Name.Contains(query)).ToList().Select(p =>
                      new SearchResult("Positions", p.Name, p.ThumbnailUrl,
                          new UrlContext("edit", "position", new RouteValueDictionary(new { Id = p.Id }))
                          ));
              });
          }
      

      在模块注册期间的后台,我们遍历每个模块并将 Func 添加到 SearchTable 中,其中的实例为:

      public class GenericFuncCollection : IEnumerable<Tuple<Type, Type, Object>>
      {
          private List<Tuple<Type, Type, Object>> objects = new List<Tuple<Type, Type, Object>>();
      
          /// <summary>
          /// Stores a list of Func of T where T is unknown at compile time.
          /// </summary>
          /// <typeparam name="T1">Type of T</typeparam>
          /// <typeparam name="T2">Type of the Func</typeparam>
          /// <param name="func">Instance of the Func</param>
          public void Add<T1, T2>(Object func)
          {
              objects.Add(new Tuple<Type, Type, Object>(typeof(T1), typeof(T2), func));
          }
      
          public IEnumerator<Tuple<Type, Type, object>> GetEnumerator()
          {
              return objects.GetEnumerator();
          }
      
          System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
          {
              return objects.GetEnumerator();
          }
      }
      

      然后当我们最终调用它时,我们用反射来做:

      var dependency = DependencyResolver.Current.GetService(search.Item1);
      var methodInfo = search.Item2.GetMethod("Invoke");
      return (IEnumerable<SearchResult>)methodInfo.Invoke(search.Item3, new Object[] { query, dependency });
      

      【讨论】:

        【解决方案7】:

        我没有在这里找到我想要的东西,但在阅读后我认为这可能是被要求的内容,因此试图回答。

        问题在于,当您使用 Dictionary 时,它是一个封闭的构造类型,并且所有元素都必须是 TValue 类型。我在很多地方都看到这个问题没有很好的答案。

        事实是我想要索引,但每个元素都有不同的类型,并且基于 TKey 的值我们已经知道类型。不是试图绕过拳击,而是试图简单地获得更优雅的访问,比如 DataSetExtensions 字段。并且不想使用动态,因为类型是已知的,只是不想要。

        一种解决方案可以是创建一个非泛型类型,它不会在类级别公开 T,因此会导致字典的 TValue 部分被关闭构造。然后撒上一个流利的方法来帮助初始化。

        public class GenericObject
        {
            private object value;
        
            public T GetValue<T>()
            {
                return (T)value;
            }
        
            public void SetValue<T>(T value)
            {
                this.value = value;
            }
        
            public GenericObject WithValue<T>(T value)
            {
                this.value = value;
                return this;
            }
        }
        
        class Program
        {
            static void Main(string[] args)
            {
                Dictionary<string, GenericObject> dict = new Dictionary<string, GenericObject>();
        
                dict["mystring"] = new GenericObject().WithValue<string>("Hello World");
                dict["myint"] = new GenericObject().WithValue<int>(1);
        
                int i = dict["myint"].GetValue<int>();
                string s = dict["mystring"].GetValue<string>();
            }
        }
        

        【讨论】:

          【解决方案8】:

          其他可能性是使用变量动态。

          例如:

          Dictionary<string, List<dynamic>> d = new Dictionary<string, List<dynamic>>(); d.Add("Key", new List<dynamic>());

          变量动态解析运行时的类型。

          【讨论】:

            【解决方案9】:

            不,但您可以使用对象而不是泛型类型。

            长答案: 当前版本的 C# 不允许您在字典中创建泛型类型的条目。您的选择是 a) 创建一个与字典相同的自定义类,但允许它接受泛型类型,或者 b) 使您的 Dictionary 采用对象类型的值。我发现选项 b 是更简单的方法。

            如果您发送特定类型的列表,那么当您处理这些列表时,您将不得不测试它是什么类型的列表。更好的方法是创建对象列表;通过这种方式,您可以输入整数、字符串或任何您想要的数据类型,并且您不必测试来查看 List 包含的对象类型。这会(大概)产生您正在寻找的效果。

            这是一个可以解决问题的简短控制台程序:

            using System;
            using System.Collections;
            using System.Collections.Generic;
            using System.Linq;
            using System.Text;
            using System.Threading.Tasks;
            
            namespace dictionary
            {
            class Program
            {
                static void Main(string[] args)
                {
                    Dictionary<string, object> dic = new Dictionary<string, object>();
                    var lstIntList = new List<object>();
                    var lstStrings = new List<object>();
                    var lstObjects = new List<object>();
                    string s = "";
            
                    lstIntList.Add(1);
                    lstIntList.Add(2);
                    lstIntList.Add(3);
            
                    lstStrings.Add("a");
                    lstStrings.Add("b");
                    lstStrings.Add("c");
            
                    dic.Add("Numbers", lstIntList);
                    dic.Add("Letters", lstStrings);
            
                    foreach (KeyValuePair<string, object> kvp in dic)
                    {
                        Console.WriteLine("{0}", kvp.Key);
                        lstObjects = ((IEnumerable)kvp.Value).Cast<object>().ToList();
            
                        foreach (var obj in lstObjects)
                           {s = obj.ToString(); Console.WriteLine(s);}
                        Console.WriteLine("");
                    }
            
            
                    Console.WriteLine("");
                    Console.WriteLine("press any key to exit");
                    Console.ReadKey();
                }//end main
            }
            }
            

            【讨论】:

              【解决方案10】:

              其中一种方法是创建一个类型为“object”的 Dictionary 值,例如:

              Dictionary&lt;string, object&gt; d = new Dictionary&lt;string, object&gt;();

              所以,这里对象数据类型被用作通用数据类型,您可以将任何内容作为值放入其中。

              【讨论】:

                【解决方案11】:

                或者可以像这样使用泛型类型

                public static void SafeUpdateInDictionary<T, L>(T DictionaryToBeUpdated, string Key, L Value) where T : Dictionary<string, L>
                    {
                        if (DictionaryToBeUpdated != null)
                        {
                            if(Value != null)
                            {
                                if (!DictionaryToBeUpdated.ContainsKey(Key))
                                    DictionaryToBeUpdated.Add(Key, Value);
                                else
                                    DictionaryToBeUpdated[Key] = Value;
                            }
                        }
                    }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-01
                  • 1970-01-01
                  • 2020-05-22
                  相关资源
                  最近更新 更多