I am studying electronic engineering, and I am a beginner in C#. I have measured data and I would like to store it in a 2 dimensional way. I thought I could make a Dictionary like this:
Dictionary<Key, string> dic = new Dictionary<Key, string>();
"Key" here is my a own class with two int variables. Now I want to store the data in this Dictionary but it doesn't work so far. If I want to read the data with the special Key, the error report says, that the Key is not available in the Dictionary.
Here is the class Key:
public partial class Key
{
public Key(int Bahn, int Zeile) {
myBahn = Bahn;
myZeile = Zeile;
}
public int getBahn()
{
return myBahn;
}
public int getZeile()
{
return myZeile;
}
private int myBahn;
private int myZeile;
}
for testing it I made something like this:
Getting elements in:
Key KE = new Key(1,1);
dic.Add(KE, "hans");
...
Getting elements out:
Key KE = new Key(1,1);
monitor.Text = dic[KE];
Has someone an idea?
You need to override methods GetHashCode and Equals in your own class to use it as a key.
class Foo
{
public string Name { get; set;}
public int FooID {get; set;}
public override int GetHashCode()
{
return FooID;
}
public override bool Equals(object obj)
{
return Equals(obj as Foo);
}
public bool Equals(Foo obj)
{
return obj != null && obj.FooID == this.FooID;
}
}
Though you could use a class as key by implementing your own Equals and GetHashCode, I would not advise to do it if you're not yet familiar with C#.
These methods will be invoked by C# internal libraries, which expect them to work exactly as per specification, handling all edge cases gracefully. If you put a bug in them, you might be in for an unpleasant head scratching session.
In my opinion, it would be no less efficient and way simpler to create a key on the spot using tried, true and tested existing types that already support hashing and comparison.
From your angular coordinates, e.g.:
int Bahn = 15;
int Zeile = 30;
You could use a string (e.g. "15,30"):
String Key (int Bahn, int Zeile) { return $"{Bahn},{Zeile}"; }
var myDict = new Dictionary<string, string>();
myDict.Add (Key(Bahn,Zeile), myString);
or a two elements tuple (e.g. <15,30>) if you need something more efficient:
Tuple<int,int> Key (int Bahn, int Zeile) { return Tuple.Create(Bahn,Zeile); }
var myDict = new Dictionary<Tuple<int, int>, string>();
myDict.Add (Key(Bahn,Zeile), myString);
or a mere combination of your two angles if the range is small enough to fit into an int (e.g. 15+30*360) if you need something even more efficient:
int Key (int Bahn, int Zeile) { return Bahn+360*Zeile; }
var myDict = new Dictionary<int, string>();
myDict.Add (Key(Bahn,Zeile), myString);
That seems a lot less cumbersome than:
class Key {
// truckloads of code to implement the class,
// not to mention testing it thourougly, including edge cases
}
var myDict = new Dictionary<Key, string>();
myDict.Add (new Key(Bahn,Zeile), myString);
Mutability of the key
Also, note that your keys must be immutable as long as they are used to index an entry.
If you change the value of Bahn or Ziel after the key has been used to add an element, you will mess up your dictionary something bad.
The behaviour is undefined, but you will most likely lose random entries, cause memory leaks and possibly crash with an exception if the internal libraries end up detecting an inconsistent state (like several entries indexed by the same key).
For instance:
var myKey = new Key(15, 30);
for (String data in row_of_data_sampled_every_10_degrees)
{
myDict.Add (myKey, data); // myKey must remain constant until the entry is removed
myKey.Bahn += 10; // changing it now spells the death of your dictionary
}
A side note on hashing angular coordinates
Now the catch is, the generic hashing functions provided for ints, strings and tuples are unlikely to produce optimal results for your specific set of data.
I would advise to start with a simple solution and only resort to specialized code if you run into actual performance issues. In which case you would probably be better off using a data structure more suited to spatial indexing (typically a quadtree in case of polar coordinates, or an octree if you want to reconstruct a 3D model from your scanner data).
For the sake of an alternate opinion and not to be disparaging of Mr. Kuroi's solution (which is a good one) here is a simple class that can be used as a key in a map as well as other uses. We use a complex class as a key because we want to know track other things. In this example, we arrive at a vertex in a graph and we want to know if we have visited it before.
<code>
public class Vertex : IComparable<Vertex>, IEquatable<Vertex>, IComparable
{
String m_strVertexName = String.Empty;
bool m_bHasVisited = false;
public Vertex()
{
}
public Vertex(String strVertexName) : this()
{
m_strVertexName = strVertexName;
}
public override string ToString()
{
return m_strVertexName;
}
public string VertexName
{
get { return m_strVertexName; }
set
{
if (!String.IsNullOrEmpty(value))
m_strVertexName = value;
}
}
public bool HasVisited
{
get { return m_bHasVisited; }
set { m_bHasVisited = value; }
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public int CompareTo(Vertex rhs)
{
if (Equals(rhs))
return 0;
return ToString().CompareTo(rhs.ToString());
}
int IComparable.CompareTo(object rhs)
{
if (!(rhs is Vertex))
throw new InvalidOperationException("CompareTo: Not a Vertex");
return CompareTo((Vertex)rhs);
}
public static bool operator < (Vertex lhs, Vertex rhs) => lhs.CompareTo(rhs) < 0;
public static bool operator > (Vertex lhs, Vertex rhs) => lhs.CompareTo(rhs) > 0;
public bool Equals (Vertex rhs) => ToString() == rhs.ToString();
public override bool Equals(object rhs)
{
if (!(rhs is Vertex))
return false;
return Equals((Vertex)rhs);
}
public static bool operator == (Vertex lhs, Vertex rhs) => lhs.Equals(rhs);
public static bool operator != (Vertex lhs, Vertex rhs) => !(lhs == rhs);
}
</code>
Related
Is my current solution, (A, B, C, D, ...).GetHashCode(), guaranteed to always be the same for tuples with "Equal" items?
public class Pair
{
public int X { get; set; }
public int Y { get; set; }
public Pair(int x, int y)
{
X = x;
Y = y;
}
public override bool Equals(object other) => Equals(other as Pair);
public virtual bool Equals(Pair other)
{
if (other is null)
{
return false;
}
if (object.ReferenceEquals(this, other))
{
return true;
}
if (this.GetType() != other.GetType())
{
return false;
}
return X == other.X && Y == other.Y;
}
public override int GetHashCode() => (X, Y).GetHashCode();
public static bool operator ==(Pair lhs, Pair rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
return false;
}
return lhs.Equals(rhs);
}
public static bool operator !=(Pair lhs, Pair rhs) => !(lhs == rhs);
}
In this code always guaranteed to print 1:
var uniquePairs = new HashSet<Pair>();
uniquePairs.Add(new Pair(2, 4));
uniquePairs.Add(new Pair(2, 4));
uniquePairs.Add(new Pair(2, 4));
uniquePairs.Add(new Pair(2, 4));
Console.WriteLine(uniquePairs.Count);
What about for a greater number of non-trivial type properties?
What are reliable GetHashCode solutions that can be used for classes like these, which guarantee equal hashodes if all (not-necessarily-int) members are the same?
People usually use some arithmetic with factors derived from the values of fields to provide a good pseudo-random distribution but will compute to the same thing if all fields are equal. Have a look at this:
General advice and guidelines on how to properly override object.GetHashCode()
Also, look at the Microsoft documentation if you want more information on the subject.
If your goal is simply to have classes whose equality is determined by fields matching rather than by reference, C# has a new record reference type you can use which does this by default. If you're using the latest version of C#/.NET this would be the way to go.
If you get into anything really complicated that has to be secure, consider looking into using some robust hash algorithms like SHA-256 ... take all your fields and turn them into a padded buffer or bytes and run them through SHA-256 (all this is found in System.Security.Cryptography). You'll take the SHA-256 output and select 4 bytes of it to produce a 32-bit integer. Collision is very, very unlikely (but of course it's still possible with only 32-bits).
I have a collection of items Foos that have a property FooPosition, and I need to quickly access Foos by their positions.
For example : retrieve a Foo which is located at X=0 and Y=1.
My first thought was to use a dictionary for that purpose nd to use the FooPosition as dictionary key. I know that every FooPosition in my collection is unique, I don't mind throwing an Exception if it is not the case.
This works well as long as Foos do not move all over the place.
But, as I figured out the hard way, and understood thanks to this and this posts, this does not work anymore if the FooPosition is updated. I shouldn't use mutable keys in a dictionary : the dictionary keeps the FooPosition HashCode in memory but does not update it when the underlying FooPosition is modified. Therefore, calling dic[Position(0,1)] gives me the Foo which was at this position when the dictionary was built.
So, I am now wondering what should I use to retrieve Foos by their positions efficiently.
By efficiently I mean not going all across the whole collection every time I query for a Foo by its position. Is there a suitable structure which would accomodate mutable keys?
Thanks for your help
EDIT
As mentioned rightfully in comments, there is a missing part in my question : I have no control over Foo Moves. The software is actually connected to another software (Excel via VSTO) via a COM Protocol which changes the FooPosition (Excel Ranges) without notifying the change.
Therefore, I cannot take take any action in case a move happens because I don't know that a change did happen.
public class FooManager
{
public void DoSomething(IList<Foo> foos) {
Dictionary<FooPosition, Foo> fooPositionDictionary = foos.ToDictionary(x => x.Position, x => x); //I know that position is unique
//Move Foos all around the place by changing their positions.
FooPosition queryPosition = new FooPosition(0, 1);
fooPositionDictionary.TryGetValue(queryPosition, out var foo1); //DOES NOT WORK
var foo2 = foos.FirstOrDefault(x => x.Position == queryPosition); //NOT EFFICIENT
//Any better idea?
}
}
public class Foo
{
public string Name { get; set; }
public FooPosition Position { get; set; }
}
public class FooPosition : IEquatable<FooPosition>
{
public int X { get; set; }
public int Y { get; set; }
public FooPosition(int x, int y)
{
X = x;
Y = y;
}
public void MoveBy(int i)
{
X = X + i;
Y = Y + i;
}
public bool Equals(FooPosition other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((FooPosition) obj);
}
public override int GetHashCode()
{
unchecked
{
return (X * 397) ^ Y;
}
}
public static bool operator ==(FooPosition left, FooPosition right)
{
return Equals(left, right);
}
public static bool operator !=(FooPosition left, FooPosition right)
{
return !Equals(left, right);
}
}
In some sense a dictionary - as any other hash-based data-storage - uses some kind of caching. In this case the hashes are cached. However as for every cache you need some constant data that does not change during the lifetime of that data-storage. If there is no such constant data, thereĀ“s no way to efficiently cache that data.
So you end up to store all items in some linear collection - e.g. a List<T>- and iterate that list again and again.
Consider counting number of occurrences of a particular letter in a word.
I came up with this solution.
string a = "aabbcdc"; //Output: a:2,b:2,c:2,d:1
var dict = new Dictionary<char, int>();
foreach(var #char in a)
{
if(dict.ContainsKey(#char))
dict[#char] = dict[#char] + 1;
else dict.Add(#char, 1);
}
foreach(var keyValuePair in dict)
Console.WriteLine("Letter = {0}, Value = {1}", keyValuePair.Key, keyValuePair.Value);
My friend argued that this solution can be improved further using Arrays as they are better than using the above Dictionary.
I guess, Dictionary lookup is O(1) (arrays also O(1), correct ?) . Any ideas how to re-write the program using arrays which performs better?
arrays also O(1), correct
Arrays are only O(1) when accessing a known offset. They are O(n) when you have to scan the array to find a particular match.
You will not out-perform a Dictionary for this type of operation in the general case.
You can leverage the accessing a known offset caveat to use arrays for this limited case. For example, if you know that you will only have input in the range 'a'..'z', you can have an array of counts
int[] counts = new int[26];
// Initialize all array elements to 0 first.
count[(int)(ch - 'a')]++;
This type of code is fragile in that a change to the range of input values breaks it. Any performance gain will not matter vs. Dictionary for almost all cases. If you think your case really needs that small performance edge, benchmark before using the more fragile implementation with arrays.
This type of operation is pretty common. I wrote a generic class for it
[Serializable]
public class OccurrenceCounter<T>
{
private Dictionary<T, int> counts;
public OccurrenceCounter()
{
Initialize(default(StreamingContext));
}
[OnDeserialized]
public void Initialize(StreamingContext context)
{
if (counts == null)
{
counts = new Dictionary<T, int>();
counts.OnDeserialization(this);
}
}
public int this[T key]
{
get
{
if (counts.ContainsKey(key))
{
return counts[key];
}
else return 0;
}
}
public bool ContainsKey(T key)
{
return counts.ContainsKey(key);
}
public int Total()
{
return counts.Sum(c => c.Value);
}
public int Count()
{
return counts.Count;
}
public int TotalFor(IEnumerable<T> keys)
{
if (keys == null) throw new ArgumentException("Parameter keys must not be null.");
HashSet<T> hash = keys.ToHashSet();
return counts.Where(k => hash.Contains(k.Key)).Sum(k => k.Value);
}
public void Increment(T key, int amount = 1)
{
if (!counts.ContainsKey(key))
{
counts.Add(key, amount); // Initialize to zero and increment
}
else
{
counts[key]+=amount;
}
}
public void Decrement(T key, int amount = 1)
{
if (!counts.ContainsKey(key))
{
counts.Add(key, -amount); // Initialize to zero and decrement
}
else
{
counts[key]-=amount;
}
}
/// <summary>
/// Could not correctly implement IEnumerable on .NET (seems to compile on Mono). Should be fixed.
/// See: http://stackoverflow.com/questions/16270722/ienumerablet-int-arity-and-generic-type-definitions
/// </summary>
/// <returns></returns>
public IEnumerable<KeyValuePair<T, int>> Iterate()
{
foreach (KeyValuePair<T, int> kvp in counts) yield return kvp;
}
}
If you are sure the letters are all within the ASCII range, i.e. 0-255, you can use an array and access it by the char value.
var arr = new int[256]; //Make sure it's initialized to 0
foreach(var #char in a)
{
arr[(int)#char] ++;
}
I have a problem using a self made IEqualityComparer and GetHashCode in a concurrent dictionary.
The class below (simplified with used two properties) works perfect when I implement it like this:
ConcurrentDictionary<TwoUintsKeyInfo,Int64> hashCodePlusIandJDict = new ConcurrentDictionary<TwoUintsKeyInfo, Int64>();
.
public class TwoUintsKeyInfo
{
public uint IdOne { get; set; }
public uint IdTwo { get; set; }
#region Implemetation of the IEqualityComparer
public class EqualityComparerTwoUintsKeyInfo : IEqualityComparer<TwoUintsKeyInfo>
{
System.Reflection.PropertyInfo[] properties;
bool propertyArraySet=false;
public int GetHashCode(TwoUintsKeyInfo obj)
{
unchecked
{
if(!propertyArraySet)
{
properties = obj.GetType().GetProperties().OrderBy(x => x.Name).ToArray();
propertyArraySet = true;
}
decimal hash = 17;
int counter=0;
foreach(System.Reflection.PropertyInfo p in properties)
{
counter++;
var value = p.GetValue(obj);
decimal unique = (decimal)Math.Pow(Math.E, counter);
hash = hash + (value == null ? unique : value.GetHashCode() * unique);
}
return 2147483647M * .001M > hash ? (int)(hash * 1000) : (int)hash;
}
}
public bool Equals(TwoUintsKeyInfo x, TwoUintsKeyInfo y)
{
return GetHashCode(x) == GetHashCode(y);
}
}
#endregion Implemetation of the IEqualityComparer
}
Now I made almost the same class, but instead of the normal IEqualityComparer interface, I made a little change, so I could generate long / int64 hascodes (because when the class hold more and more properties, we encountered multiple values with the same hashcode)
So I wanted to reduce the changes of getting the same hascode. Therefore I wanted to use bigger numbers and if possible multiple by 10000 to get some of the decimals in on the action as well.
therefore I created this interface:
public interface IEqualityComparerInt64<in T>
{
bool Equals(T x, T y);
Int64 GetHashCode(T obj);
}
and altered the property class so it looks like this:
public class TwoUintsKeyInfoInt64
{
public uint IdOne { get; set; }
public uint IdTwo { get; set; }
#region Implemetation of the IEqualityComparer
public class EqualityComparerTwoUintsKeyInfoInt64 : IEqualityComparerInt64<TwoUintsKeyInfoInt64>
{
System.Reflection.PropertyInfo[] properties;
bool propertyArraySet=false;
decimal _upperThreshold,_lowerThreshold;
public EqualityComparerTwoUintsKeyInfoInt64()
{
_upperThreshold = long.MaxValue * .0001M;
_lowerThreshold = -long.MaxValue * .0001M;
}
public long GetHashCode(TwoUintsKeyInfoInt64 obj)
{
unchecked
{
if(!propertyArraySet)
{
properties = obj.GetType().GetProperties().OrderBy(x => x.Name).ToArray();
propertyArraySet = true;
}
decimal hash = 17;
int counter=0;
foreach(System.Reflection.PropertyInfo p in properties)
{
counter++;
var value = p.GetValue(obj);
decimal unique = (decimal)Math.Pow(Math.E, counter);
hash = hash + (value == null ? unique : value.GetHashCode() * unique);
}
return _upperThreshold > hash && _lowerThreshold < hash ? (long)(hash * 10000) : (long)hash;
}
}
public bool Equals(TwoUintsKeyInfoInt64 x, TwoUintsKeyInfoInt64 y)
{
return GetHashCode(x) == GetHashCode(y);
}
}
#endregion Implemetation of the IEqualityComparer
}
GetHashCode worked fine. So far no problem.
But...when I try to add a IEqualityComparer to the concurrentdictionary like this:
ConcurrentDictionary<TwoUintsKeyInfoInt64,Int64> hashCodePlusIandJDict = new ConcurrentDictionary<TwoUintsKeyInfoInt64, Int64>(new TwoUintsKeyInfoInt64.EqualityComparerOneUintAndTwoStringKeyInfo());
I get this error:
Error 3 Argument 1: cannot convert from
'HasCodeTestForUniqueResult.TwoUintsKeyInfoInt64.EqualityComparerOneUintAndTwoStringKeyInfo'
to
'System.Collections.Generic.IEqualityComparer' D:\Users\mldz\Documents\visual
studio
2012\HashCodeTestForUniqueResult\HashCodeTestForUniqueResult\Form1.cs 109 140 HashCodeTestForUniqueResult
I understand that there's a conflict between the int type of the default System.Collections.Generic.IEqualityComparer and my long / int64 result from my own GetHashCode generator. But is there any way to solve this and be able to use long HashCodes?
Kind regards,
Matthijs
P.S. the code above is just to test it and replicate the problem.
According to this you cannot use long hash codes, so the answer to the question is no.
But you can have unique combinations instead of unique values; the solution is to implement a partitioning system, meaning have a dictionary of dictionaries, like:
public class MyClass
{
Dictionary<uint, Dictionary<uint, Int64>> PartDict;
Int64 ReadValue(uint id1, uint id2)
{
return (PartDict[id1])[id2];
}
void AddValue(uint id1, uint id2, Int64 value)
{
Dictionary<uint, Int64> container;
if (!PartDict.TryGetValue(id1, out container))
{
container = new Dictionary<uint, Int64>();
PartDict.Add(id1, container);
}
container.Add(id2, value);
}
}
This way you will have a list of hash codes and each hash code will have again a list of hash codes, the combination being unique. Any reading and writing will be done in 2 steps though (to consider in case you want unique hash for performance).
Hope it helps.
I have a field in a database (whose schema I can't change) which contains a specific set of values. Let's call them H, M, and L. H stands for High, M for Medium, and L is for Low. In C# I'd like to be able to reference these values in a typesafe way, but one that is also readable in code.
Currently there's a lot of this pattern littering the repository:
public static class Priority
{
public const string High = "H";
public const string Medium = "M";
public const string Low = "L";
}
Which does provide the readability but isn't typesafe and could potentially be dangerous if lowercase values make their way into the database (unlikely but not impossible).
Is there a better way to handle this pattern?
You can implement this as a combination of an enum and a static class encapsulating logic for it, like this:
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");
}
}
Now you can use Priorities.GetPriority(codeFromDatabase) to make an element of Priority enumeration from a DB code, and call
priority.GetCode()
to obtain a code for writing a Priority back to the database.
There are two ways I'd deal with this, depending on the situation.
The first is to use an enum and a Dictionary<TKey, TValue> to map a character to an entry in the 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];
}
}
Another way to do this would be to create a struct which creates itself in public static readonly fields.
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; }
}
Method 1:
Pros: You only have to define the enum the result will automatically update. You can access both the full name (enumInstance.ToString()) and the code.
Cons: You need to explicitly call conversion methods to change between the char and Priority.
Method 2:
Pros: The type will implicitly convert to char, and can be cast from char.
Cons: You have to update both the calls to register and the enum to add or modify entries. You cannot access the full name of the field.
Both cons on method two can be resolved easily. The first can be resolved by using reflection to discover all public fields. The second by either adding it as parameter to the constructor or also through reflection.
Using method 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
Using method 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
Personally I think this solution is much cleaner than relying on two switches and the definition. Performance wise (it won't really matter unless you have an incredibly large database) it'll perform very similar to the switch statement. A switch statement in the right condition will be compiled an in-code hashmap, just like a Dictionary<TKey, TValue> is a hashmap.
If you want to have multi-character strings just change char to string.