Creating a non-static version of the "dictionary" based on the compiler, where keys are types

There is a very simple trick that creates a dictionary-like structure where keys are types. The structure acts like a Dictionary<Type, T?> , Where the keys are Type objects and the values ​​are instances of the corresponding types.

This wonderful structure is as fast as just a variable or an array, since the "search" is done once by the / JITter compiler, and the link to the correct value is compiled into your program.

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

You can work with this structure as follows:

 MyDict<string>.Value = MyDict<int>.Value.ToString(); 

The problem is that this "dictionary" is global. The only way to create different dictionaries is to create different classes.

How to create a similar (fastest “search”, not boxing) static structure? (Without code generation.)

It is simply said: I want to have several Dictionary<Type, object> that are not related to search, casting and boxing.

+4
source share
8 answers

Ark-kun uses generics to generate unique types at compile time. With a common type, any static elements are unique to this particular closed generic type. Thus, it is processed as quickly as the standard search for static elements.

The above use is equivalent to something like this:

 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, types are "static" (in that you cannot define more than one of this path), so I don’t know a way to spoof this and maintain the same performance as a statically compiled element search.

It is best (in other words, I think) to create a generic instance type that wraps its own vocabulary that uses System.Type for its keys and System.Object for its values, which you must do boxing / casting when inserting / retrieving values.

EDIT: here's a simple implementation carrying a dictionary:

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

Thinking more about this, one could get more syntax similar to a property using ExpandoObject ( http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx ) through some stupidity, but I feel that it would be rather offensive, and I can only assume terribly prone to runtime errors. (plus this will not give you anything at compile time)

EDITx2: If you really want to have different sets of values, you can nest it in another generic type:

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

Using for example:

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

But then the initial type int and bool in this case will become “magic” and without meaning, plus you will need to provide a unique type for a separate set of values ​​that you would like to use. Also, you could not pass it and change it as an instance variable, rather, it would be statically available (if you have access to type T ). Therefore, perhaps you can declare minimally visible types whose names make sense and use them:

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

Despite this, I think it's pretty stupid, and I would not recommend it.

+1
source

Here's an approach that extends the method described in the question:

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

Now we can use TypeDict as follows:

 void X() { var a = new TypeDict(); var b = new TypeDict(); a.Set<int>(1); a.Set<double>(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<int>()); Console.WriteLine(a.Get<double>()); Console.WriteLine(a.Get<string>()); Console.WriteLine(); Console.WriteLine(b.Get<int>()); Console.WriteLine(b.Get<double>()); Console.WriteLine(b.Get<string>()); } 
+2
source

More complicated version. I don't know if this is closer:

Define a common dictionary:

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

Then the repository to store these dictionaries:

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

And finally, you can use this repository:

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

In this example, there is a MyDictionary<T> box for an object .

On the other hand, if you are working with some specific types, you cannot use the repository class at all. But use separate dictionaries.

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

However, this is the same as working with

 Dictionary<string, int> intDict = new Dictionary<string, int>(); 

Thus, the MyRepository class can be edited to use Dictionary<string, T> instead of MyDictionary<T> .

+1
source
Answer to

@Konstantin made me remember that there really is a very fast search method - indexing an array. This crude PoC code shows a variation of the required structure.

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

This code can be used as follows:

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

The problem with this solution is that memory usage is due to the lack of a repository. It also increases the cost of installation for the first time.

+1
source

Try the following:

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

and use it:

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

This class can store up to one value of type T But perhaps there may be corner cases with derived classes and interfaces. You can check whether it is suitable for your needs and, possibly, change it as necessary, if it is close to what you need as a whole.

0
source

What you are looking for is not possible in C #. The language does not support a container that can store several objects of different types, but provides a search method that does not include casting, boxing or unpacking. You can do something similar using macros in C ++ or using the javascript language, where the type structure can be changed at runtime.

0
source

The usage example that you describe is very closely related to the purpose for which ConditionalWeakTable<TKey,TValue> was added in .NET 4.0. For this purpose, you should include such a table in a static general class, and then for each object of the class that should contain a link to an element of a certain type, you should store in this table the type of the link to the object, which is supposed to contain the element along with the link to an element or by reference to a simple object-holder of the element (note that entries in ConditionalWeakTable will evaporate when the object ceases to exist, but otherwise they will not change, so if you want a modified association , you need to create an object to store it).

0
source

Creating an @phoog example with the @supercat clause

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

All Articles