创建基于编译器的“字典”的非静态版本,其中键是类型

有一个非常简单的技巧,它创建了一个类似字典的结构,其中键是类型。 该结构的行为类似于Dictionary ,其中键是Type对象,值是相应类型的实例。

这个奇妙的结构和变量或数组一样快,因为“查找”只由编译器/ JITter执行一次,并且正确的值引用被编译到程序中。

  public static class MyDict { public static T Value { get; set; } } 

您可以像这样使用该结构:

 MyDict.Value = MyDict.Value.ToString(); 

问题是这个“字典”是全球性的。 创建不同字典的唯一方法是创建不同的类。

如何创建一个类似的(最快的“查找”,没有拳击)非静态结构? (没有代码生成。)

简单地说:我希望有多个Dictionary类似的对象,没有查找成本,转换和装箱。

Ark-kun使用generics在编译时基本上生成唯一类型。 对于generics类型,任何静态成员对于特定的封闭generics类型都是唯一的。 这样,它的处理速度与标准静态成员查找一样快。

以上用法等同于:

 public static class MyDict_String { public static string Value { get; set; } } public static class MyDict_Int32 { public static int Value { get; set; } } MyDict_String.Value = MyDict_Int32.Value.ToString(); 

AFAIK,类型是“静态的”(因为你不能用这种方式定义多个),所以我不知道如何欺骗这个并保持静态编译的成员查找的相同性能。

否则(我认为)最好的选择是创建一个通用实例类型,它包装自己的字典,使用System.Type作为其键, System.Object作为其值,在插入/检索值时必须执行装箱/转换。

编辑:这是一个包装字典的简单实现:

 public class MyTypedDict { private Dictionary Values = new Dictionary(); public T Get() { object untypedValue; if (Values.TryGetValue(typeof(T), out untypedValue)) return (T)untypedValue; return default(T); } public void Set(T value) { Values[typeof(T)] = value; } } 

考虑更多,有可能通过一些tomfoolery使用ExpandoObjecthttp://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx )实现更像属性的语法,但是我觉得这会非常滥用,我只能假设非常容易出现运行时错误。 (加上它在编译时不会给你任何东西)

EDITx2:如果你真的想拥有不同的值集 ,可以它嵌套在另一个generics类型中:

 public static class ValueSets { public static class MyDict { public static U Value { get; set; } } } 

使用方式如下:

 ValueSets.MyDict.Value = "Hello "; ValueSets.MyDict.Value = "World!"; string helloworld = ValueSets.MyDict.Value + ValueSets.MyDict.Value; Console.WriteLine(helloworld);//Hello World! 

但是在这种情况下,初始类型intbool变得“神奇”而没有意义,而且你需要为你想要使用的每个不同值集提供一个唯一类型。 另外,您无法传递它并作为实例变量进行修改,而是可以静态访问(只要您有权使用类型T )。 所以也许您可以声明具有含义的最小可见类型并使用它们:

 internal class MyFirstWords {} internal class MySecondWords {} ValueSets.MyDict.Value = "Hello "; ValueSets.MyDict.Value = "World!"; string helloworld = ValueSets.MyDict.Value + ValueSets.MyDict.Value; Console.WriteLine(helloworld);//Hello World! 

无论如何,我觉得这很古怪,我不推荐它。

这是一种扩展问题中描述的方法的方法:

 public class TypeDict { public T Get() { return MyDict.Values[this]; } public void Set(T value) { MyDict.Values[this] = value; } private static class MyDict { public static Dictionary Values { get; private set; } static MyDict() { Values = new Dictionary(); } } } 

现在我们可以像这样使用TypeDict:

 void X() { var a = new TypeDict(); var b = new TypeDict(); a.Set(1); a.Set(3.14); a.Set("Hello, world!"); //Note that type inference allows us to omit the type argument b.Set(10); b.Set(31.4); b.Set("Hello, world, times ten!"); Console.WriteLine(a.Get()); Console.WriteLine(a.Get()); Console.WriteLine(a.Get()); Console.WriteLine(); Console.WriteLine(b.Get()); Console.WriteLine(b.Get()); Console.WriteLine(b.Get()); } 

一个更复杂的版本。 不知道它是否更接近:

定义通用字典:

 public class MyDictionary { Dictionary dict; public MyDictionary() { dict = new Dictionary(); } public T this[string name] { get { if (dict.ContainsKey(name)) return dict[name]; else return default(T);//or throw } set { dict[name] = value; } } } 

然后是存储这些词典的存储库:

 public class MyRepository { List repo; public MyRepository() { repo = new List(); } public void Add(string name, T value) { if (!repo.OfType>().Any()) repo.Add(new MyDictionary()); var dict = repo.OfType>().FirstOrDefault(); dict[name] = value; } public T GetValue(string name) { if (!repo.OfType>().Any()) return default(T);//or throw else { var dict = repo.OfType>().FirstOrDefault(); return dict[name]; } } } 

最后你可以使用这个存储库:

  MyRepository repo = new MyRepository(); repo.Add("A", 1); repo.Add("B", 1); int i = repo.GetValue("A") + repo.GetValue("B"); 

在这个例子中,还有MyDictionary装箱object

另一方面,如果您正在使用某些类型,则可能根本不使用该存储库类。 但是使用单独的词典。

 MyDictionary intDict = new MyDictionary(); intDict["A"] = 1; intDict["B"] = 2; int i = intDict["A"] + intDict["B"]; 

然而,它与使用相同

 Dictionary intDict = new Dictionary(); 

因此可以编辑MyRepository类以使用Dictionary而不是MyDictionary

@ Konstantin的回答让我记得实际上有一种非常快速的查找方法 – 数组索引。 这个粗略的PoC代码显示了所需结构的变体。

  public class TypeDictionary { static int _maxId = 0; int _id; static class Store{ internal static List Values = new List(); } public TypeDictionary() { _id = _maxId++; } public T GetValue() { return Store.Values[_id]; } public void SetValue(T value) { while(Store.Values.Count < _id) { Store.Values.Add(default(T)); } Store.Values[_id] = value; } } 

此代码可以使用如下:

  var dict1 = new TypeDictionary(); dict1.SetValue("my string"); string result = dict1.GetValue(); 

此解决方案的问题在于存储库不稀疏导致的内存使用情况。 这也使第一次设置值更加昂贵。

试试这个:

 public class MyDictionary { List values; public MyDictionary() { values = new List(); } public T GetValue() { return values.OfType().FirstOrDefault(); } public bool Add(T value) { if (values.OfType().Any()) return false; else { values.Add(value); return true; } } } 

并使用它:

 var md = new MyDictionary(); md.Add("!!!"); string s = md.GetValue(); 

此类可以存储最多一个T类型的值。 但是我想可能会出现派生类和接口的情况。 您可以检查,如果它符合您的需要,并且可能根据您的需要进行修改,如果它接近您的需求。

你在C#中找不到你想要的东西。 该语言不支持可以存储多个不同类型对象的容器,但提供了一种不涉及转换,装箱或拆箱的查找方法。 你可以用C ++中的宏来完成这样的事情,或者通过像javascript这样的语言来实现类型的结构可以在运行时更改。

您描述的用例非常符合将ConditionalWeakTable添加到.NET 4.0的目的。 出于您描述的目的,您将在静态generics类中包含这样的表,然后对于应该包含对特定类型的项的引用的每个类对象,您将在该类型的表中存储对该对象的引用。包含项目以及对项目的引用,或者对简单项目持有者对象的引用(请注意,当一个对象不再存在时, ConditionalWeakTable中的条目将消失,但是否则是不可变的,所以如果你想要一个可变的关联你需要创建一个对象来保存它)。

以@phicog的建议为基础构建@phoog的例子

 public class TypeDict { public T Get() where T : class { T value; InnerDict.Values.TryGetValue(this, out value); return value; } public void Set(T value) where T : class { var cwt = InnerDict.Values; // lock+remove+add https://github.com/dotnet/coreclr/issues/4545 lock (cwt) { cwt.Remove(this); cwt.Add(this, value); } } private static class InnerDict where T : class { public static ConditionalWeakTable Values { get; private set; } static InnerDict() { Values = new ConditionalWeakTable(); } } }