我在数据库中有一个字段(其架构我无法更改),其中包含一组特定的值。我们称他们为H
,M
和L
。 H
代表高,M
代表中,L
代表低。在C#
中,我希望能够以类型安全的方式引用这些值,但在代码中也可以读取。
目前有很多这种模式乱丢在存储库中:
public static class Priority
{
public const string High = "H";
public const string Medium = "M";
public const string Low = "L";
}
哪个提供了可读性,但不是类型安全的,如果小写值进入数据库,可能会有危险(不太可能但不是不可能)。
有没有更好的方法来处理这种模式?
答案 0 :(得分:2)
您可以将其实现为enum
和静态类封装逻辑的组合,如下所示:
public enum Priority { High, Medium, Low }
public static class Priorities {
public static string GetCode(this Priority priority) {
switch (priority) {
case Priority.High: return "H";
case Priority.Medium: return "M";
case Priority.Low: return "L";
}
throw new ArgumentException("priority");
}
public static Priority GetPriority(string priorityCode) {
switch (priorityCode) {
case "H": return Priority.High;
case "M": return Priority.Medium;
case "L": return Priority.Low;
}
throw new ArgumentException("priorityCode");
}
}
现在,您可以使用Priorities.GetPriority(codeFromDatabase)
从数据库代码中创建Priority
枚举元素,然后调用
priority.GetCode()
获取用于将Priority
写回数据库的代码。
答案 1 :(得分:1)
根据具体情况,我有两种方法可以解决这个问题。
第一种方法是使用enum
和Dictionary<TKey, TValue>
将字符映射到enum
中的条目。
enum Priority : byte
{
High,
Medium,
Low
}
static class Priorities
{
private static Dictionary<char, Priority> _toPriority = new Dictionary<char, Priority>();
private static Dictionary<Priority, char> _fromPriority = new Dictionary<Priority, char>();
static Priorities()
{
var priorities = Enum.GetNames(typeof(Priority));
var values = (Priority[])Enum.GetValues(typeof(Priority));
for (var i = 0; i < priorities.Length; i++)
{
_toPriority.Add(priorities[i][0], values[i]);
_fromPriority.Add(values[i], priorities[i][0]);
}
}
public static Priority GetPriority(string field)
{
Priority res;
if (!TryGetPriority(field, out res))
throw new ArgumentException("Invalid priority on field.", "field");
return res;
}
public static bool TryGetPriority(string field, out Priority priority)
{
if (field == null || field.Length == 0) { priority = default(Priority); return false; }
return _toPriority.TryGetValue(field[0], out priority);
}
public static char GetCode(Priority priority)
{
return _fromPriority[priority];
}
}
另一种方法是创建一个struct
,它在公共静态只读字段中创建自己。
struct Priority
{
public static readonly Priority High = new Priority('H');
public static readonly Priority Medium = new Priority('M');
public static readonly Priority Low = new Priority('L');
static Priority()
{
register(High);
register(Medium);
register(Low);
}
public static bool TryGetPriority(char code, out Priority priority)
{
return _map.TryGetValue(code, out priority);
}
public static Priority GetPriority(char code)
{
Priority priority;
if (!TryGetPriority(code, out priority))
throw new ArgumentException("Code doesn't represent an existing priority.", "code");
return priority;
}
public override int GetHashCode()
{
return _code.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is Priority)) return false;
return ((Priority)obj)._code == _code;
}
public override string ToString()
{
return _code.ToString();
}
public static implicit operator char(Priority @this) { return @this._code; }
public static explicit operator Priority(char code)
{
Priority result;
if (!_map.TryGetValue(code, out result))
throw new InvalidCastException();
return result;
}
private static readonly Dictionary<char, Priority> _map = new Dictionary<char, Priority>();
private static void register(Priority p)
{
_map.Add(char.ToLowerInvariant(p._code), p);
_map.Add(char.ToUpperInvariant(p._code), p);
}
private readonly char _code;
private Priority(char code) { _code = code; }
}
方法1:
优点:您只需要定义enum
,结果就会自动更新。您可以访问全名(enumInstance.ToString()
)和代码
缺点:您需要明确调用转换方法,以便在char
和Priority
之间进行更改。
方法2:
优点:该类型将隐式转换为char
,并且可以从char
投射。
缺点:您必须更新对register
和enum
的调用才能添加或修改条目。您无法访问该字段的全名。
方法二的两个缺点都可以轻松解决。第一个可以通过使用反射来发现所有公共字段来解决。第二种方法是将其作为参数添加到构造函数中,或者也通过反射。
使用方法1:
Priority p = Priority.High; // Assign literal
MessageBox.Show(p.ToString()); // High
MessageBox.Show(Priorities.GetCode(p).ToString()); // H
Priority p = Priorities.GetPriority('L'); // Cast from character
MessageBox.Show(p.ToString()); // Low
MessageBox.Show(Priorities.GetCode(p).ToString()); // L
Priority p; // Safe assigning
if (!Priorities.TryGetPriority('M', out p))
return;
MessageBox.Show(p.ToString()); // Medium
MessageBox.Show(Priorities.GetCode(p).ToString()); // M
使用方法2:
Priority p = Priority.High; // Assign literal
MessageBox.Show(p.ToString()); // H
Priority p = (Priority)'L'; // Cast from character
MessageBox.Show(p.ToString()); // L
Priority p; // Safe assigning
if (!Priority.TryGetPriority('M', out p))
return; // Handle invalid scenario
MessageBox.Show(p.ToString()); // M
就我个人而言,我认为这个解决方案比依赖两个交换机和定义更清晰。性能方面(除非你拥有一个非常大的数据库,否则它并不重要)它的性能与switch语句非常相似。正确条件下的switch语句将被编译为in-code hashmap,就像Dictionary<TKey, TValue>
是一个hashmap一样。
如果您想要使用多字符字符串,只需将char
更改为string
。