How to create hierarchical classes / enums to be used as a key in collection in .NET C#
I have a collection of stuffs in a generic Dictionary<string, object>()
.
The key is a string, and I'm attempting to come up with a way to use a hierarchical data structure instead of a string as they key.
So instead of doing this:
object value = myDictionary["App1.Account1.SomeSetting"];
I want to be able to do this:
object value = myDictionary[App1.Account1.SomeSetting];
(notice lack of quotes around the key.
This means the dictionary will become Dictionary<{something}, object>()
.
What's the best way to define that {something}? It would be strongly typed and would works like this:
App1 - could be any number of applications App1 could contain many accounts (App1.Account1) Accounts can contain many settings (App1.Account1.Setting1)
The {something} data-structu开发者_运维技巧re would be hard coded with all possible levels/settings, but I can't think of what it could be organized like to still work as a key.
EDIT: See my answer below for the solution. Hopefully it helps someone else.
I think I understand what you want: you want your "flat" Dictionary of keys and values to be made accessible via specifying a compiler-checked identifier set that evaluates to the string that is the key of the Dictionary.
Before going down this road, I must ask, why don't you simply store the values in this hierarchical structure that would produce your keys? If you're going to go to the trouble of setting this structure up, it seems rather superfluous to do it just to evaluate it into a string key.
Instead, you could have the relatively simple structure:
public class App
{
public Account[] Accounts {}
}
public class Account
{
public decimal ApprovedAmount {get;set;}
public int ContractLength {get;set;}
}
... and then either define constant App1, App2, etc, or set these classes to be serializable to-from XML, or mapped to an ORM, so they can be saved in some relatively program-agnostic, human-consumable state.
Trees are a natural fit for encoding hierarchical information. Use a tree.
This was ultimately the solution I've implemented, with some generics and some pre-defined hierarchy of classes to replace the "settings" tree.
Usage:
Settings secSettings = new Settings();
secSettings.Add(new SettingNames.App1.Screen1.MaxBillAmountToConsolidate(), 50m);
secSettings.Add(new SettingNames.App2.SingleSetting(), new DateTime(2010, 11, 25));
secSettings.Add(new SettingNames.App1.Screen2.CanEditField2(), false);
secSettings.Add(new SettingNames.App1.Screen2.CanEditField3(), true);
int amount;
if (secSettings.TryGetValue(new SettingNames.App1.Screen1.MaxBillAmountToConsolidate(), out amount))
{
Print(amount);
}
DateTime expiredDate;
if (secSettings.TryGetValue(new SettingNames.App2.SingleSetting(), out expiredDate))
{
Print(expiredDate);
}
bool permission;
if (secSettings.TryGetValue(new SettingNames.App1.Screen2.CanEditField2(), out permission))
{
Print(permission);
}
if (secSettings.TryGetValue(new SettingNames.App1.Screen2.CanEditField3(), out permission))
{
Print(permission);
}
Settings classes (each class naturally in its own file, but all in the same namespace):
namespace Security
{
/// <summary>
/// Base class from which all Security Setting instances must inherit
/// </summary>
public abstract class SettingNameBase
{
/// <summary>
/// Returns fully qualified type name of the instance of this class as a string
/// </summary>
public override string ToString()
{
return this.GetType().FullName;
}
}
[DataContract]
public class Settings
{
[DataMember]
private Dictionary<String, Object> settings = new Dictionary<String, Object>();
public void Add<T>(SettingNameBase name, T value)
{
if (!settings.ContainsKey(name.ToString()))
{
settings.Add(name.ToString(), value);
}
else
{
throw new ArgumentException(string.Format("A setting with the key '{0}' already exists.", name.ToString().Replace("+", ".")));
}
}
public bool TryGetValue<T>(SettingNameBase name, out T value)
{
bool dictContainsKey = false;
if (dictContainsKey = settings.ContainsKey(name.ToString()))
{
try
{
value = (T)Convert.ChangeType(settings[name.ToString()], typeof(T));
}
catch (InvalidCastException ex)
{
string errMsg = string.Format("Invalid cast of value '{0}' to type of {1} in Setting.Value<T>() method.",
settings[name.ToString()], typeof(T).FullName);
throw new InvalidCastException(errMsg, ex);
}
}
else
{
value = default(T);
}
return dictContainsKey;
}
}
public class SettingNames
{
/// <summary>
/// Setting for a user
/// </summary>
public class User
{
public class Name : SettingNameBase { }
public class ID : SettingNameBase { }
public class Password : SettingNameBase { }
}
/// <summary>
/// Setting for App1
/// </summary>
public class App1
{
public class Screen1
{
public class CanEditField1 : SettingNameBase { }
public class MaxBillAmountToConsolidate : SettingNameBase { }
}
public class Screen2
{
public class CanEditField2 : SettingNameBase { }
public class CanEditField3 : SettingNameBase { }
}
}
/// <summary>
/// Setting for App2
/// </summary>
public class App2
{
public class SingleSetting : SettingNameBase { }
}
}
}
精彩评论