Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class TypedKey<T>
- {
- public string Name { get; private set; }
- public TypedKey(string name)
- {
- Name = name;
- }
- }
- static class DictionaryExtensions
- {
- public static T Get<T>(this IDictionary<string, object> dictionary, TypedKey<T> key)
- {
- return (T)dictionary[key.Name];
- }
- public static void Set<T>(this IDictionary<string, object> dictionary, TypedKey<T> key, T value)
- {
- dictionary[key.Name] = value;
- }
- }
- private static readonly TypedKey<int> AgeKey = new TypedKey<int>("age");
- …
- dictionary.Get(AgeKey) > 18
- dictionary.Set(AgeKey, age)
- static class DictionaryExtensions
- {
- public static T Get<T>(this IDictionary<string, object> dictionary, string key)
- {
- return (T)dictionary[key];
- }
- public static bool TryGet<T>(this IDictionary<string, object> dictionary,
- string key, out T value)
- {
- object result;
- if (dictionary.TryGetValue(key, out result) && result is T) {
- value = (T)result;
- return true;
- }
- value = default(T);
- return false;
- }
- public static void Set(this IDictionary<string, object> dictionary,
- string key, object value)
- {
- dictionary[key] = value;
- }
- }
- int age = 20;
- dictionary.Set("age", age);
- // ...
- age = dictionary.Get<int>("age");
- // or the safe way
- if (dictionary.TryGet("age", out age)) {
- Console.WriteLine("The age is {0}", age);
- } else {
- Console.WriteLine("Age not found or of wrong type");
- }
- public class Property<T>
- {
- Dictionary<object, object> _dict;
- public Property (Dictionary<object, object> dict)
- {
- _dict = dict;
- }
- public T Value {
- get { return (T)_dict[this]; }
- set { _dict[this] = value; }
- }
- }
- private static readonly Dictionary<object, object> _properties =
- new Dictionary<object, object>();
- private static readonly Property<int> _age = new Property<int>(_properties);
- ...
- _age.Value > 18
- _age.Value = age
- public interface ITypedKey<T>
- {
- string Name { get; }
- }
- public class TypedKey<T> : ITypedKey<T>
- {
- private readonly string name;
- public TypedKey(string name)
- {
- if (name == null)
- {
- throw new ArgumentNullException("name");
- }
- this.name = name;
- }
- public string Name
- {
- get
- {
- return this.name;
- }
- }
- }
- public interface IPropertyBag
- {
- T Get<T>(ITypedKey<T> key);
- void Set<T>(ITypedKey<T> key, T value);
- void Remove<T>(ITypedKey<T> key);
- }
- public class PropertyBag : IPropertyBag
- {
- private readonly IDictionary<string, object> bag;
- public PropertyBag()
- {
- this.bag = new Dictionary<string, object>();
- }
- public PropertyBag(IDictionary dict)
- {
- if (dict == null)
- {
- throw new ArgumentNullException("dict");
- }
- this.bag = new Dictionary<string, object>(dict.Count);
- foreach (DictionaryEntry kvp in dict)
- {
- this.bag.Add(new KeyValuePair<string, object>(kvp.Key as string, kvp.Value));
- }
- }
- public T Get<T>(ITypedKey<T> key)
- {
- if (key == null)
- {
- throw new ArgumentNullException("key");
- }
- return (T)this.bag[key.Name];
- }
- public void Set<T>(ITypedKey<T> key, T value)
- {
- if (key == null)
- {
- throw new ArgumentNullException("key");
- }
- this.bag[key.Name] = value;
- }
- public void Remove<T>(ITypedKey<T> key)
- {
- if (key == null)
- {
- throw new ArgumentNullException("key");
- }
- this.bag.Remove(key.Name);
- }
- }
- private static readonly TypedKey<int> AgeKey = new TypedKey<int>("age");
- private static readonly TypedKey<string> BadAgeKey = new TypedKey<string>("age");
- dictionary.Set(BadAgeKey, “foo”);
- ...
- // this would throw
- dictionary.Get(AgeKey);
- public class TypeSafeKey<T> { }
- public class TypeSafeKeyValuePairBag
- {
- public T GetItemOrDefault<TKey, T>(T defaultValue = default(T)) where TKey : TypeSafeKey<T>
- => TryGet(typeof(TKey), out T result) ? result : defaultValue;
- public T GetItemOrDefault<T>(TypeSafeKey<T> key, T defaultValue = default(T))
- => TryGet(key?.GetType() ?? throw new ArgumentNullException(nameof(key)), out T result) ? result : defaultValue;
- public void SetItem<TKey, T>(T value) where TKey : TypeSafeKey<T>
- => m_values[typeof(TKey)] = value;
- public void SetItem<T>(TypeSafeKey<T> key, T value)
- => m_values[key?.GetType() ?? throw new ArgumentNullException(nameof(key))] = value;
- public T GetItem<TKey, T>() where TKey : TypeSafeKey<T>
- => Get<T>(typeof(TKey));
- public T GetItem<T>(TypeSafeKey<T> key)
- => Get<T>(key?.GetType() ?? throw new ArgumentNullException(nameof(key)));
- private bool TryGet<T>(Type type, out T value)
- {
- if (m_values.TryGetValue(type, out object obj))
- {
- value = (T)obj;
- return true;
- }
- value = default(T);
- return false;
- }
- private T Get<T>(Type type)
- => TryGet(type, out T result) ? result : throw new KeyNotFoundException($"Key {type.FullName} not found");
- private Dictionary<Type, object> m_values = new Dictionary<Type, object>();
- }
- // You need to declare a Type for each key that you want to use, all though the types can defined anywhere
- // They don't need to be known to the assembly where TypeSafeKeyValuePairBag is defined, but they do need to
- // be known to the any code that is setting or getting any given key. So even though the declaration of
- // these class could be spread throughout the source tree, since each is a type they are forced to be unique
- public class KeyHight : TypeSafeKey<int> { }
- public class KeyWidth : TypeSafeKey<int> { }
- public class KeyName : TypeSafeKey<string> { }
- // A static class, with static public members would reduce the number of instances of objects that needed to be created for repeated reads/writes.
- // You would need to create these in a lazy fashion if you had many of them. And since only their type matters, you don’t need to worry about locking, since two different instances of the same Type would function as the same key.
- public static class Keys
- {
- public static KeyHight KeyHight { get; } = new KeyHight();
- public static KeyWidth KeyWidth { get; } = new KeyWidth();
- public static KeyName KeyName { get; } = new KeyName();
- }
- ...
- TypeSafeKeyValuePairBag bag = new TypeSafeKeyValuePairBag();
- // Accessing hard coded keys
- //Using Generic Type Parameters: The compiler can't infer the value Type from the Key Type, which means listing them both
- bag.SetItem<KeyHight, int>(5);
- //Passing the key as a parameter
- bag.SetItem(Keys.KeyWidth, 10);
- bag.SetItem(Keys.KeyName, "foo");
- // Selecting which keys to access at run time
- int value = 1;
- foreach(var key in new TypeSafeKey<int>[] { Keys.KeyHight, Keys.KeyWidth })
- {
- value *= bag.GetItem(key);
- }
- Console.WriteLine($"{bag.GetItem<KeyName, string>()}'s area is {value}");
- private bool TryGet<T>(Type type, out T value)
- {
- if (m_values.TryGetValue(type, out object obj))
- {
- value = (T)obj;
- return true;
- }
- Type baseType = type.BaseType;
- if (baseType != typeof(TypeSafeKey<T>))
- {
- return TryGet(baseType, out value);
- }
- value = default(T);
- return false;
- }
- public class KeyLevel0Value : TypeSafeKey<int> { }
- public class KeyLevelA1Value : KeyLevel0Value { }
- public class KeyLevelA2Value : KeyLevelA1Value { }
- public class KeyLevelB1Value : KeyLevel0Value { }
- public class KeyLevelB2Value : KeyLevelB1Value { }
- ...
- bag.SetItem<KeyLevelA1Value, int>(5);
- // This will first check the value for LevelA2. After not finding it, it will check LevelA2, and that value will be returned
- Console.WriteLine(bag.GetItem<KeyLevelA2Value, int>());
- // This will first check the value for LevelB2, LevelB1, and finally Level0, and since none are set it will return default
- Console.WriteLine(bag.GetItemOrDefault<KeyLevelB2Value, int>());
Add Comment
Please, Sign In to add comment