'eGame' is an ambiguous reference between 'GameLogParser.eGame' and 'GuildStats_Shared.eGame' - c#

Is it posible to tell the compiler this is the same enum, so it can be defined both places?
public enum eGame
{
Error,
AoC,
Rift,
}

It's not the same enum though. The title indicates you have two distinct types
GameLogParser.eGame
GuildStats_Shared.eGame
Now, if they really contain the same enum names / values, you can do a simple conversion between the two:
GameLogParser.eGame firstEnumType = eGame.Error;
int firstValue = (int)firstEnumType;
GuildStats_Shared.eGame second = (GuildStats_Shared.eGame)Enum.ToObject(typeof(GuildStats_Shared.eGame), firstValue);
//second should be "Error"
In addition, your title indicates a compile time error because you probably haven't fully namespaced your reference; if you just do this:
eGame x = eGame.Error;
The compiler doesnt know which type to use. Do something like this:
GameLogParser.eGame = GameLogParser.eGame.Error;

Related

Properly identifying enums with same underlying value

Assume I have this enum defined, where several members have the same underlying value:
enum Number
{
One = 1,
Eins = 1,
Uno = 1
}
According to MSDN documentation:
If multiple enumeration members have the same underlying value and you attempt to retrieve the string representation of an enumeration member's name based on its underlying value, your code should not make any assumptions about which name the method will return.
So for example,
var number = Number.One;
Console.WriteLine(number);
gives me the following output:
Eins
Printing all enum members,
Console.WriteLine($"{Number.One} {Number.Eins} {Number.Uno}");
yields the following output:
Eins Eins Eins
However, taking the nameof of each member,
Console.WriteLine($"{nameof(Number.One)} {nameof(Number.Eins)} {nameof(Number.Uno)}");
gives the following result:
One Eins Uno
So apparently the enum members are separable. Can I take advantage of this separation, i.e. is there any way I can assign a specific Number member to a variable and consistently have that same member returned whenever the variable is accessed?
So apparently the enum members are separable
Well, that's not entirely true... They are only separable at compile time.
You see, nameof is actually an expression evaluated at compile time. It is a constant expression. This can be proved by assigning a nameof expression to a const:
const string a = nameof(Number.One);
It compiles.
Trying to get the string representation of a enum value using string interpolation on the other hand, is evaluated at runtime, so this does not compile:
const string a = $"{Number.One}";
At runtime, the enum cases are not separable, so the answer to:
is there any way I can assign a specific Number member to a variable and consistently have that same member returned whenever the variable is accessed?
is "no".
The only possibility I see to always return an expected enum name is to create a 2nd enum next to your first of the underlying type and with the same values but limit the members to those that you expect (and make sure there are no shared values). Then you can cast from one to the other and use the new enum in your refactored code that relies on specific/expected members.
SomeMethod
Console.WriteLine("{0:G}", (KnownNumber)Number.Eins); // > One
Console.WriteLine("{0:G}", (KnownNumber)Number.Uno); // > One
Console.WriteLine("{0:G}", (KnownNumber)Number.One); // > One
Enums.cs
public enum Number
{
One = 1,
Eins = 1,
Uno = 1
}
public enum KnownNumber
{
One = 1,
Two = 2,
Three = 3
}
Fiddle
I wrote something that should get it work
public static class NumberExtension
{
private static Dictionary<int, string> pointers = new Dictionary<int, string>();
public static unsafe void SetValue(this Number source, string value)
{
if (pointers.ContainsKey((int)&source))
pointers[(int)&source] = value;
else
pointers.Add((int)&source, value);
}
public static unsafe string GetValue(this Number source)
{
if (pointers.ContainsKey((int)&source))
return pointers[(int)&source];
return source.ToString();
}
}
And to use:
Number num = default(Number);
num.SetValue(nameof(Number.Uno));
Console.WriteLine(num.GetValue());
However, it looks like a kind of 'hack' and I do NOT recommend it. It would be better if you look for a better solution.

How to define a catch-all enum value when casting from an integer?

I've got a enum type defined in my C# code that corresponds to all possible values for the NetConnectionStatus field in Win32_NetworkAdapter WMI table, as documented here.
The documentation shows that the integers 0 through 12 each have a unique status name, but then all integers between 13 and 65,535 are lumped into one bucket called "Other." So here's my code:
[Serializable]
public enum NetConnectionStatus
{
Disconnected = 0,
Connecting = 1,
Connected = 2,
Disconnecting = 3,
HardwareNotPresent = 4,
HardwareDisabled = 5,
HardwareMalfunction = 6,
MediaDisconnected = 7,
Authenticating = 8,
AuthenticationSucceeded = 9,
AuthenticationFailed = 10,
InvalidAddress = 11,
CredentialsRequired = 12,
Other
}
This works fine for the values that are not Other. For instance, I can do this:
var result = (NetConnectionStatus) 2;
Assert.AreEqual(NetConnectionStatus.Connected, result);
But for anything in that higher numeric range, it doesn't work so great. I would like it if I could do this:
var result = (NetConnectionStatus) 20;
Assert.AreEqual(NetConnectionStatus.Other, result);
But right now that result variable gets assigned the literal value 20 instead of Other. Is there some out-of-the-box way of accomplishing this, something akin to Parse() but for integers instead of strings, or perhaps some special attribute I'm unaware of? I would prefer to not write my own wrapper method for this if there is already a good way to accomplish this.
If you have a string value, then the closest thing I can think of is to use Enum.TryParse:
NetConnectionStatus result;
if (Enum.TryParse(stringValue, out result) == false)
result = NetConnectionStatus.Other;
For an integer value that you're casting, you can use:
result = (NetConnectionStatus)integerValue;
if (Enum.GetValues(typeof(NetConnectionStatus)).Contains(result) == false)
result = NetConnectionStatus.Other;
Not really ideal, but in C# enums aren't much more than fancy names for integral values, so it's valid to stuff an integer value not in the defined values of the enums into a value of that enum type.
This solution will handle negative numbers, or cases where you have gaps in your enum values more elegantly than doing numerical comparisons.
it would be nice but no. How about
var result = (NetConnectionStatus) 20;
Assert.IsTrue(result >= (int)NetConnectionStatus.Other);
.NET does not such thing as a "any other" enumeration value bucket. Technically, enumeration (enum) is a pretty set of named constants of some underlying type (which is one of following: sbyte, short, int, long and their unsigned counterparts). You can cast an enum value to/from a corresponding type without any losses, as in this example:
enum TestEnum:int // Explicitly stating a type.
{
OnlyElement=0
}
class Program
{
static void Main(string[] args)
{
// Console.WriteLine implicitly calls ToString of the TestEnum.OnlyElement.
Console.WriteLine("OnlyElement == {0}", TestEnum.OnlyElement);
//TestEnum.OnlyElement equals to 0, as demonstrated by this casting:
Console.WriteLine("(int)OnlyElement == {0}", (int)TestEnum.OnlyElement);
//We can do it in reverse...
Console.WriteLine("(TestEnum)0 == ",(TestEnum)0);
// But what happens when we try to cast a value, which is not
// representable by any of enum's named constants,
// into value of enum in question? No exception is thrown
// whatsoever: enum variable simply holds that value, and,
// having no named constant to associate it with, simply returns
// that value when attempting to "ToString"ify it:
Console.WriteLine("(TestEnum)5 == {0}", (TestEnum)5); //prints "(TestEnum)5 == 5".
Console.ReadKey();
}
}
I'd like to repeat it again, enum in .NET is simply a value of the underlying type with some nice decorations like overriden ToString method and flags checking (look here or here if you want to know more about flags). You cannot have an integer with only 14 values like "0..12 and everything else", and so you cannot have such enum. In your example, NetConnectionStatus.Other simply receives single literal value (I assume it would most probably be '13', as the next available positive value of underlying type - however it actually depends on the compiler) as any other enumeration constant would do if not specified explicitly - and, obviously, it does not become a bucket.
However, there are options to achieve simple equation checks for integers/bytes/shorts/longs - and enums alike. Consider this extension method:
static bool IsOther(this NetConnectionStatus A)
{
return (A < (NetConnectionStatus)0) || (A > (NetConnectionStatus)12);
}
Now you can have a simple assertion like this:
var result = (NetConnectionStatus)10;
Trace.Assert(result.IsOther()); //No assertion is triggered; result is NetConnectionStatus.AuthenticationFailed
and
var result = (NetConnectionStatus)20;
Trace.Assert(result.IsOther()); //Assertion failed; result is undefined!
(Of course you can replace IsOther method with IsNotOther, overload it and pretty much anything else you could do with a method.)
Now there is one more thing. Enum class itself contains a method called IsDefined, which allows you to avoid checks for specific enum's value boundaries (<0, >12), therefore preventing unwanted bugs in case enum values would ever be added/removed, at the small performance cost of unboxing and checking each value in enum for a match (I'm not sure how this works under the hood though, I hope these checks are optimized). So your method would look like this:
static bool IsOther(NetConnectionStatus A)
{
return !Enum.IsDefined(typeof(NetConnectionStatus), A);
}
(However, concluding from enum's name, it seems like you want to make a network application/server, and for these performance might be of very great importance - but most probably I'm just being paranoid and this will not be your application's bottleneck. Stability is much more of concern, and, unless you experience real troubles with performance, it is considered to be much better practice to enable as much stability&safety&portability as possible. Enum.IsDefined is much more understandable, portable and stable than the explicit boundaries checking.)
Hope that helps!
Thanks everyone for the replies. As confirmed by all of you, there is indeed no way to do this out-of-the-box. For the benefit of others I thought I'd post the (custom) code I ended up writing. I wrote an extension method that utilizes a custom attribute on the enum value that I called [CatchAll].
public class CatchAll : Attribute { }
public static class EnumExtensions
{
public static T ToEnum<T, U>(this U value) where T : struct, IConvertible where U : struct, IComparable, IConvertible, IFormattable, IComparable<U>, IEquatable<U>
{
var result = (T)Enum.ToObject(typeof(T), value);
var values = Enum.GetValues(typeof(T)).Cast<T>().ToList();
if (!values.Contains(result))
{
foreach (var enumVal in from enumVal in values
let info = typeof(T).GetField(enumVal.ToString())
let attrs = info.GetCustomAttributes(typeof(CatchAll), false)
where attrs.Length == 1
select enumVal)
{
result = enumVal;
break;
}
}
return result;
}
}
So then I just have to apply that [CatchAll] attribute to the Other value in the enum definition. Then I can do things like this:
int value = 13;
var result = value.ToEnum<NetConnectionStatus, int>();
Assert.AreEqual(NetConnectionStatus.Other, result);
And this:
ushort value = 20;
result = value.ToEnum<NetConnectionStatus, ushort>();
Assert.AreEqual(NetConnectionStatus.Other, result);

Best way to assign property using enum c#

How can I effective extend enum to have more than 2 options.
I am reading events from a file, line-by-line.
I have a constructor
public enum EventType
{ A,D }
public class Event
{
public EventType Type { get; set; }
}
I assigned Type property like this:
Type = tokens[2].Equals("A") ? EventType.A : EventType.D,
where token[2] is the string that holds values like "A".
This works fine when there are only A and D, but I want to have 2 more types; say R and C. When I add them to enum field, how can I get the type? The above is giving compilation errors as if using Type as a variable.
I appreciate your immediate help!
Thanks
There are really only three sensible ways to go about this:
Enum.TryParse
If the tokens will always correspond exactly to your enum members, you can use Enum.TryParse:
EventType type;
if (Enum.TryParse(tokens[2], out type)) {
Type = type;
}
else { /* token does not exist as an enum member */ }
This approach is the simplest, but it's probably slower than the next one and it also has another drawback: the author of the code that provides tokens[2] and the author of the enum must always keep their code in sync.
Use a dictionary
var dict = new Dictionary<string, EventType>
{
{ "A", EventType.A },
{ "D", EventType.D },
// more items here
}
Type = dict[tokens[2]]; // no error checking, please add some
This requires some setup, but it's probably the fastest and it also allows accounting for changes in the input strings and/or enum values.
Use an attribute
Alternatively, you can annotate your enum members with a custom attribute and write a helper method that uses reflection to find the correct member based on the value of this attribute. This solution has its uses but it's the least likely candidate; most of the time you should prefer one of the two alternatives.
You can want to use Enum.Parse to get the matching value:
Type = Enum.Parse(typeof(EventType), tokens[2])
If tokens[2] is not defined in EventType, Enum.Parse throws an exception, so you can use Enum.IsDefined to check if there is an enum value for the string:
Enum.IsDefined(typeof(EventType), tokens[2])
You may use Enum.Parse to parse a string. For error handling you may use Enum.GetNames(typeof(EventType)) and iterate over the returned string array, which contains all possible names of the enum.
var type = (EventType)Enum.Parse(typeof(EventType), tokens[2]);
EventType et;
switch(tokens[2])
{
case "A":
et=EventType.A;
break;
case "B":
et=EventType.B;
break;
case "C":
et=EventType.C;
break;
}
return et;

Enum assigned value parse c#

Okay, for whatever reason I can't seem to figure this little problem out.
I have the following enum:
public enum EFeedType
{
TypeOne = 1,
TypeTwo = 2
}
Now, I am going to be getting the numeric value from a database. Well, I need to cast the int value from the DB to the enum type:
EDIT:
The database type is integer, so I do not need to cast from string.
END EDIT
EFeedType feedType = (EFeedType) feedId;
However, when I do this, and pass in value of 2 I get the following error:
Instance validation error: '2' is not a valid value for [Namespace Goes Here].EFeedType.
Any thoughts on what I might be doing wrong or missing?
EDIT
Here is the code I am using:
//GetFeed will return an int value which is pulled from the database
int feedId = new FeedEngine().GetFeed("FeedName");
//Convert the ID to the Enum
EFeedType feedType = (EFeedType) feedId;
//Set the User Control FeedType Enum to the enum
FeedControl.FeedType = feedType;
//Show the user control
FeedControl.Visible = true;
EDIT - More Info
Okay, after seeing JSkeet's response, I see that If my feedId = 1 then it will set the enum value to TypeTwo instead of TypeOne like it should. Maybe I need a Default = 0 value in my enum for this to work? But there has to be a better way, because what if my values are not in sequence.
Your error won't be coming from the line you showed it on. I strongly suspect it's coming from this line:
FeedControl.FeedType = feedType;
My guess is that that property is doing some validation - and that it doesn't know about the relevant value.
EDIT: Note that if you do want to find out if a value is valid, use Enum.IsDefined. Enum.Parse will not throw an exception for an incorrect numeric value, so long as it's in the right range.
Have you really posted the exact code of the enum? Because if it doesn't explicitly specify the "= 1" and "= 2" it will autoincrement from 0, and that will be your problem.
If you could demonstrate all of this with a short but complete program it would really help. There's no need to go to the database, and no need to do anything with a user control.
I just tried the following and it worked perfectly. You might want to break your code out into a little test project to see what's going on.
// enum definition
public enum EFeedType {
TypeOne = 1,
TypeTwo = 2,
}
// usage
private void TestEnum() {
Int32 feedId = 2;
EFeedType stuff = (EFeedType)Enum.Parse(typeof(EFeedType), feedId.ToString());
Response.Write(stuff.ToString());
}

Trouble with an enumeration as a key in a Dictionary collection

I have a scenario where I'm using a Dictionary to hold a list of transaction types that a certain system accepts. The key in the Dictionary is an enum field, the value is an int.
At some point in the system, we're going to want to do something like this:
sqlCommand.Parameters.AddWithValue("#param", LookupDictionary[argument.enumField]);
When we look up the field in the dictionary, we're going to get the correct integer value to feed to the database. I've thought about actually using the enum int value for this, but that's not exactly right. We're interacting with a system where we need to feed a magic number in to represent the kind of update we're doing.
The code above works just fine. I have an initializer method that adds the known types:
LookupDictionary = new Dictionary<mynamespace.myproject.myclass.enumType, int>();
LookupDictionary.Add(enumType.entry1, 4);
LookupDictionary.Add(enumType.entry2, 5);
LookupDictionary.Add(enumType.entry3, 6);
This code also works fine.
But up above, before I actually get in to using the LookupDictionary, I validate that the request being made is actually set to an enum value we support. That's LookupDictionary's main reason to be, it holds the valid ones (there are valid enum entries that this method doesn't work with).
This is the code that doesn't work: the system fails to recognize that the enums match. In the debugger, I can see that the entries list in LookupDictionary does show that it has the value for entry2 - it just calls it like that, entry2. The incoming enumField on the other hand has the full namespace; mynamespace.myproject.myclass.enumType.entry2 - I imagine this is why it doesn't see them as being the same.
if (!LookupDictionary.ContainsKey(argument.enumField))
{
throw new InvalidOperationException("argument.enumField not valid in blahMethod.");
}
Did I mention that this is being passed across a WCF service? But I'm not using an auto-generated proxy ... both projects on both sides of the wire share the types as a project reference, and I build up my channel client in code.
Any ideas? Am I doing it wrong? Do Dictionaries with Enums as keys not work well? Is it a WCF thing?
Note: thanks for the suggestions regarding setting the enums up to contain the magic int. I wanted to set those in a configuration, however, as its possible that the "magic numbers" 4 5 and 6 might change down the road. So if I code them in to the enum as suggested:
public enum MyEnum
{
MyValue1 = 4,
MyValue2 = 5,
MyValue3 = 6
}
I lose the ability to write a method that sets up the magic numbers in the future at run time; instead it would require a code change.
Instead of using the enum as the key, use the integer representation of the enum.
For instance:
LookupDictionary = new Dictionary<int, int>();
LookupDictionary.Add((int)enumType.entry1, 4);
LookupDictionary.Add((int)enumType.entry2, 5);
LookupDictionary.Add((int)enumType.entry3, 6);
That way, you can use the same 'ContainsKey' method of the dictionary. I'm not sure this is much better performance than a List<int>
You shouldn't need a lookup table here at all:
public enum MyEnum
{
MyValue1 = 4,
MyValue2 = 5,
MyValue3 = 6
}
// Sample usage
MyEnum firstEnum = MyEnum.MyValue1;
int intVal = (int)firstEnum; // results in 4
// Enum Validation
bool valid = Enum.IsDefined(typeof(MyEnum), intVal); // results in true
Can you considered typing your enumeration explicitly as int (or whatever the underlying type is) and then setting the value of each of your enumerations to the database value? You've already tightly coupled the enumeration to the database, so either the relationship will be dictated in C# (current hard-coding) or by SQL (perhaps a proc that returns the ID as well as a string that can be parsed into an enumeration.)
Using the assumption that your enumeration is an int...
enum enumType {
entry1 = 4,
entry2 = 5,
entry3 = 6
}
When adding your parameter you would then just cast as the enum's underlying type.
sqlCommand.Parameters.AddWithValue("#param", (int)argument.enumField);
You can explicitly set the values of the enum using the syntax
enum ArgumentTypes {
Arg1 = 1;
Arg2 = 3;
Arg3 = 5;
}
You don't need to keep each value sequential in the enum for this syntax to work.
To validate that only parameters that are valid for the method are ever used, try this sample code. Note I suggest using an ArgumentException over an InvalidOperationException in this context.
public void DoDbWork(ArgumentTypes argType, object otherParameter)
{
if (argType == ArgumentTypes.Arg3) {
throw new ArgumentException("Argument of value " + argType + " is not valid in this context", "argType");
}
// Handle db transaction here
}
To add the int value as the parameter:
cmd.Parameters.AddWithValue("#paramName", (int)argType);

Categories