我可以创建通用类型字典吗?

时间:2009-03-17 15:28:45

标签: c# generics

我想创建一个Dictionary对象,使用字符串Keys,保存通用类型的值。我想它看起来像这样:

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. 语法是什么?
  3. 非常感谢,
    乔恩

9 个答案:

答案 0 :(得分:30)

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

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

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

(集合本身不会是通用的 - 除非你想让密钥类型通用。)

但是,您无法以强类型方式从中提取值,因为编译器不会知道您用于特定键的类型。如果您将密钥设置为类型本身,则以略微更好的情况结束,但现有集合仍然不支持该情况。这就是我原来的答案所回应的情况。

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

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

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

答案 1 :(得分:23)

这样的事情会起作用吗?

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

基本上它会为你背后的所有幕后包装。

答案 2 :(得分:3)

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

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

但是你确实在编译时失去了类型检查。

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

我没有找到我在这里寻找的东西,但在阅读后我认为这可能是被要求的所以试图回答。

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

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

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

,

答案 5 :(得分:1)

如果您认为自己是C#4

,那么词典怎么样?
Dictionary<string, dynamic> Dict = new Dictionary<string, dynamic>();

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

答案 6 :(得分:0)

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

Dictionary<string, object> d = new Dictionary<string, object>();

因此,这里的object数据类型用作通用数据类型,您可以将其中的任何内容作为值放入。

答案 7 :(得分:0)

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

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

如果发送特定类型的列表,则当您处理这些列表时,必须进行测试以查看它是哪种类型。更好的方法是创建对象列表。这样,您可以输入整数,字符串或所需的任何数据类型,而不必进行测试即可查看列表所保存的对象类型。 (大概)会产生您想要的效果。

下面是一个简短的控制台程序,可以完成此操作:

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

enter image description here

答案 8 :(得分:0)

我使用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>>();
        return dictionary.TryGetValue(original, out clone);
    }
}