我想创建一个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个问题:
非常感谢,
乔恩
答案 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>();
答案 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
}
}
答案 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);
}
}