C# 我可以创建泛型字典吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/654752/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can I Create a Dictionary of Generic Types?
提问by Jon Artus
I'd like to create a Dictionary object, with string Keys, holding values which are of a generic type. I imagine that it would look something like this:
我想创建一个带有字符串键的 Dictionary 对象,其中包含通用类型的值。我想它看起来像这样:
Dictionary<string, List<T>> d = new Dictionary<string, List<T>>();
And enable me to add the following:
并使我能够添加以下内容:
d.Add("Numbers", new List<int>());
d.Add("Letters", new List<string>());
I know that I can do it for a list of strings, for example, using this syntax:
我知道我可以为字符串列表执行此操作,例如,使用以下语法:
Dictionary<string, List<string>> d = new Dictionary<string, List<string>>();
d.Add("Key", new List<string>());
but I'd like to do it for a generic list if possible...
但如果可能的话,我想为一个通用列表做这件事......
2 questions then:
那么2个问题:
- Is it possible?
- What's the syntax?
- 是否可以?
- 语法是什么?
Many thanks,
Jon
非常感谢,
乔恩
采纳答案by Jon Skeet
EDIT: Now I've reread the question...
编辑:现在我重读了这个问题......
You can't do this, but a custom collection would handle it to some extent. You'd basically have a generic Add
method:
您不能这样做,但自定义集合会在一定程度上处理它。你基本上有一个通用的Add
方法:
public void Add<T>(string key, List<T> list)
(The collection itself wouldn'tbe generic - unless you wanted to make the key type generic.)
(集合本身不会是通用的 - 除非您想让键类型通用。)
You couldn't extractvalues from it in a strongly typed manner though, because the compiler won't know which type you've used for a particular key. If you make the key the type itself, you end with a slightlybetter situation, but one which still isn't supported by the existing collections. That's the situation my original answer was responding to.
但是,您无法以强类型方式从中提取值,因为编译器不知道您为特定键使用了哪种类型。如果您将键作为类型本身,您会以稍微好一点的情况结束,但现有集合仍然不支持这种情况。这就是我最初的答案所回应的情况。
EDIT: Original answer, when I hadn't quite read the question correctly, but which may be informative anyway...
编辑:原始答案,当我没有完全正确阅读问题时,但无论如何可能会提供信息......
No, you can't make one type argument depend on another, I'm afraid. It's just one of the things one might want to express in a generic type system but which .NET's constraints don't allow for. There are always going to be such problems, and the .NET designers chose to keep generics relativelysimple.
不,恐怕你不能让一种类型的参数依赖于另一种类型的参数。这只是人们可能想要在泛型类型系统中表达但 .NET 的约束不允许的事情之一。总是会有这样的问题,.NET 设计者选择保持泛型相对简单。
However, you can write a collection to enforce it fairly easily. I have an example in a blog postwhich only keeps a single value, but it would be easy to extend that to use a list.
回答by JoshBerke
Would something like this work?
这样的东西会起作用吗?
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;
}
}
Basically it wraps all the casting behind the scenes for you.
基本上它为您包装了幕后的所有演员。
回答by Skizz
I prefer this way of putting generic types into a collection:
我更喜欢这种将泛型类型放入集合的方式:
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.
}
}
But you do lose the type checks at compile time.
但是您确实会在编译时丢失类型检查。
回答by Lucent Fox
We're using lots of reflection to create an extensible administration tool. We needed a way to register items in the global search in the module definition. Each search would return results in a consistent way, but each one had different dependencies. Here's an example of us registering search for a single module:
我们使用大量反射来创建可扩展的管理工具。我们需要一种方法来在模块定义中的全局搜索中注册项目。每次搜索都会以一致的方式返回结果,但每个搜索都有不同的依赖关系。这是我们注册搜索单个模块的示例:
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 }))
));
});
}
In the background during module registration, we iterate over every module and add the Func to a SearchTable with an instance of:
在模块注册期间的后台,我们遍历每个模块并将 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();
}
}
Then when we finally call it, we do it with reflection:
然后当我们最终调用它时,我们用反射来做:
var dependency = DependencyResolver.Current.GetService(search.Item1);
var methodInfo = search.Item2.GetMethod("Invoke");
return (IEnumerable<SearchResult>)methodInfo.Invoke(search.Item3, new Object[] { query, dependency });
回答by pacoke
Other posibility it's to use the variable dynamic.
其他可能性是使用变量动态。
For example:
例如:
Dictionary<string, List<dynamic>> d = new Dictionary<string, List<dynamic>>();
d.Add("Key", new List<dynamic>());
Dictionary<string, List<dynamic>> d = new Dictionary<string, List<dynamic>>();
d.Add("Key", new List<dynamic>());
the variable dynamic resolve the type on runtime.
变量 dynamic 在运行时解析类型。
回答by Hank Hatch
I didn't find what I was looking for here but after reading I think it might be what is being asked for so an attempt to answer.
我没有在这里找到我要找的东西,但在阅读后我认为这可能是所要求的,所以试图回答。
The problem is that when you use Dictionary it is a closed constructed type and all elements must be of the TValue type. I see this question in a number of places without a good answer.
问题是当您使用 Dictionary 时,它是一个封闭的构造类型,并且所有元素都必须是 TValue 类型。我在很多地方都看到这个问题,但没有很好的答案。
Fact is that I want indexing but each element to have a different type and based on the value of TKey we already know the type. Not trying to get around the boxing but trying to simply get more elegant access something like DataSetExtensions Field. And don't want to use dynamic because the types are known and it is just not wanted.
事实是我想要索引但每个元素都有不同的类型,并且基于 TKey 的值我们已经知道类型。不是试图绕过拳击,而是试图简单地获得更优雅的访问权限,例如 DataSetExtensions Field。并且不想使用动态,因为类型是已知的,只是不想要。
A solution can be to create a non generic type that does not expose T at the class level and therefore cause the TValue part of the dictionary to be closed constructed. Then sprinkle in a fluent method to help initialization.
一个解决方案可以是创建一个非泛型类型,它不会在类级别公开 T 并因此导致字典的 TValue 部分被关闭构造。然后撒上一个fluent方法来帮助初始化。
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>();
}
}
回答by Awais Ali
One of the way is to create a Dictionary value with type "object" like:
一种方法是创建一个类型为“object”的 Dictionary 值,例如:
Dictionary<string, object> d = new Dictionary<string, object>();
Dictionary<string, object> d = new Dictionary<string, object>();
So, here object datatype is used as a generic datatype, you can put anything in this as a value.
因此,这里对象数据类型用作通用数据类型,您可以将任何内容作为值放入其中。
回答by lugao
How about Dictionary<string, dynamic>
? (assuming you're on C# 4)
怎么样Dictionary<string, dynamic>
?(假设您使用的是 C# 4)
Dictionary<string, dynamic> Dict = new Dictionary<string, dynamic>();
回答by technoman23
No, but you can use object instead of generic type.
不,但您可以使用 object 而不是泛型类型。
Long answer: The current version of C# will not allow you to make entries of generic type in a dictionary. Your options are either a) create a custom class that is the same as a dictionary except allow it to accept generic types, or b) make your Dictionary take values of type object. I find option b to be the simpler approach.
长答案:当前版本的 C# 不允许您在字典中创建泛型类型的条目。您的选择是 a) 创建一个与字典相同的自定义类,但允许它接受泛型类型,或者 b) 使您的字典采用对象类型的值。我发现选项 b 是更简单的方法。
If you send lists of specific types, then when you go to process the lists you will have to test to see what kind of list it is. A better approach is to create lists of objects; this way you can enter integers, strings, or whatever data type you want and you don't necessarily have to test to see what type of object the List holds. This would (presumably) produce the effect you're looking for.
如果您发送特定类型的列表,那么当您去处理列表时,您将必须测试以查看它是哪种类型的列表。更好的方法是创建对象列表;通过这种方式,您可以输入整数、字符串或任何您想要的数据类型,而不必测试以查看 List 包含的对象类型。这将(大概)产生您正在寻找的效果。
Here is a short console program that does the trick:
这是一个可以解决问题的简短控制台程序:
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
}
}
回答by Orace
I came to a type safe implementation using 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);
}
}
It can be used like this:
它可以像这样使用:
/// <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);
}
}