Related
If I have the following enum:
public enum ReturnValue{
Success = 0,
FailReason1 = 1,
FailReason2 = 2
//Etc...
}
Can I avoid casting when I return, like this:
public static int main(string[] args){
return (int)ReturnValue.Success;
}
If not, why isn't an enum value treated as an int by default?
enums are supposed to be type safe. I think they didn't make them implicitly castable to discourage other uses. Although the framework allows you to assign a constant value to them, you should reconsider your intent. If you primarily use the enum for storing constant values, consider using a static class:
public static class ReturnValue
{
public const int Success = 0;
public const int FailReason1 = 1;
public const int FailReason2 = 2;
//Etc...
}
That lets you do this.
public static int main(string[] args){
return ReturnValue.Success;
}
EDIT
When you do want to provide values to an enum is when you want to combine them. See the below example:
[Flags] // indicates bitwise operations occur on this enum
public enum DaysOfWeek : byte // byte type to limit size
{
Sunday = 1,
Monday = 2,
Tuesday = 4,
Wednesday = 8,
Thursday = 16,
Friday = 32,
Saturday = 64,
Weekend = Sunday | Saturday,
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}
This enum can then be consumed by using bitwise math. See the below example for some applications.
public static class DaysOfWeekEvaluator
{
public static bool IsWeekends(DaysOfWeek days)
{
return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
}
public static bool IsAllWeekdays(DaysOfWeek days)
{
return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
}
public static bool HasWeekdays(DaysOfWeek days)
{
return ((int) (days & DaysOfWeek.Weekdays)) > 0;
}
public static bool HasWeekendDays(DaysOfWeek days)
{
return ((int) (days & DaysOfWeek.Weekend)) > 0;
}
}
There is no implicit cast because the enum does not have to use int as the underlying type. If your enum used a uint as the underlying type, for instance, there is no implicit cast from uint to int.
The c# enum is useless.
You can avoid casting from your type AND constrain the values that can be explicitly cast to your type by making a sealed class, and providing implicit/explicit conversion operators.
Provide an implicit operator for converting from your type to a generic int so you don't have to cast.
Provide an explicit operator for converting from an int to your type, which throws an error if the integer fails to meet the constraint, such as (int x) => (x >= 0 && x <= 2).
If using this technique, create a generic immutable base class such as ConstrainedNumber<T>, which has a constructor that accepts a T value and delegate for the constraint: delegate bool NumberConstraint<T>(T value). The constructor should run the value through the constraint delegate, and throw an exception if it fails to meet the constraint. The base class should also take care of the implicit conversion operation to T, and should handle equality by overloading object.Equals(object) and object.GetHashCode(), defining == and != operators for the type ConstrainedNumber<T>, and implementing IEquatable<T> and IEquatable<ConstrainedNumber<T>>. I also recommend defining an copy constructor for the base class, and all derived types. Cloning can then be implemented cleanly in the base class by retrieving the copy constructor via reflection, but this is entirely optional. You can figure out the ConstrainedNumber<T> implementation yourself, unless I've already posted it on stackoverflow somewhere.
You can provide named static readonly values in your derived ConstrainedNumber, so that you can access them just like an enum.
public sealed class ReturnValue: ConstrainedNumber<int>
{
public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3);
public static readonly ReturnValue Success = new ReturnValue(0);
public static readonly ReturnValue FailReason1 = new ReturnValue(1);
public static readonly ReturnValue FailReason2 = new ReturnValue(2);
private ReturnValue( int value ): base( value, constraint ) {}
private ReturnValue( ReturnValue original ): base (original) {} //may be used to support IClonable implementation in base class
public static explicit operator ReturnValue( int value )
{
switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public.
{
case 0: return Success;
case 1: return FailReason1;
case 2: return FailReason2;
}
throw new ArgumentException( "Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value" );
}
}
You could use this technique for any constraint. For example, a class called EvenNumber may have a constraint that returns true if the given number is even. In that case, you'd just make your constructors public, and simplify your static conversion operator to just return a new EvenNumber, instead of switching to return one of the limited existing instances.
It could be used like this:
EvenNumber x = (EvenNumber)2;
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber." A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model
int z = x; //implicit conversion, no cast necessary;
Enums and ints are simply not implicitly castable as per the spec (except for the literal 0, which is allowed for comparison tests / assignments / etc). The explicit cast is all that is needed, though.
Strangely enough, this is not specific to the .NET Framework, but just to C#. As the other commenters have already pointed out, in C# this is basically a specification of the language. The same is not true in VB.NET.
Check out the MSDN reference page for Enums in VB.NET. Note that you can specify the data type of an enumeration at Enum declaration time.
That means, if you really don't want to litter your code with casts to (int), you could write your enumeration in VB.NET, declare it as an integer, then use that Enum from C#.
Remember how they told us computers would make our lives so much simpler? :)
No, you can't avoid casting; as to why there's no implicit conversion, I don't know, but there's not.
You can ascribe this behaviour to the basic intention behind creating Enumerations... to create a set of named constants that can only have specified (or default) values depending on the underlying type.
There are two separate issues to consider, as related to your question:
An Enum value cannot be treated as an int by default because then you would be able to provide any integer and there would be no compile time check to validate that the provided integer does in fact exist as a value in the Enumeration.
Casting becomes necessary since you are trying to convert from the governing type (of type YourCustomEnum which derives from the System.Enum class) to the underlying type, i.e., int or byte, etc.
Risking a Necromancer batch, I still like to post a possibility that didn't come up yet: To use a helper class (resp. struct) that converts implicitly into int and the enum type:
internal struct AutoCaster<T1, T2> {
private T1 Value1 { get; }
private T2 Value2 { get; }
public AutoCaster(T1 value1) {
Value1 = value1;
Value2 = (T2)(object)value1;
}
public AutoCaster(T2 value2) {
Value1 = (T1)(object)value2;
Value2 = value2;
}
public static implicit operator AutoCaster<T1, T2>(T2 input) {
return new AutoCaster<T1, T2>(input);
}
public static implicit operator AutoCaster<T1, T2>(T1 input) {
return new AutoCaster<T1, T2>(input);
}
public static implicit operator T1(AutoCaster<T1, T2> input) {
return input.Value1;
}
public static implicit operator T2(AutoCaster<T1, T2> input) {
return input.Value2;
}
}
As the Main needs a fix return type (int or void) it does not look that elegant in your example, but for other purposes it works just fine:
public static int Main(string[] args) {
return Main2(args);
}
private static AutoCaster<int, ReturnValue> Main2(string[] args) {
return ReturnValue.FailReason2;
}
How about using static Members of a Class?
//enum DocInfos { DocName, DocNumber, DocVersion};
public class DocInfos
{
public static int DocName = 0;
public static int DocNumer = 1;
public static int DocVersion = 2;
}
...
Doc = new string[DocInfos.DocVersion];
// Treffer
Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2])
...
In my childish naïvety, I decided to build generic SetFlag and UnsetFlag extension methods for Enums, so no one has to read and reread and rereread the bitwise operators all over my code:
public static void SetFlag<T>(this T en, T flag) where T : Enum {
en |= flag;
}
and
public static void UnsetFlag<T>(this T en, T flag) where T : Enum
{
en &= ~flag;
}
Now I get the errors that operator |= is not applicable to types T and T and operator ~ is not applicable to type T.
I think that I have to change T to the type "Enum with HasFlags". Is this the true root of the problem, and how would I change that?
Here are my methods. As I said in the comments to the question, I strongly discourage from using this methods in production code:
public static T SetFlag<T>(this T en, T flag) where T : struct, IConvertible
{
int value = en.ToInt32(CultureInfo.InvariantCulture);
int newFlag = flag.ToInt32(CultureInfo.InvariantCulture);
return (T)(object)(value | newFlag);
}
public static T UnsetFlag<T>(this T en, T flag) where T : struct, IConvertible
{
int value = en.ToInt32(CultureInfo.InvariantCulture);
int newFlag = flag.ToInt32(CultureInfo.InvariantCulture);
return (T)(object)(value & ~newFlag);
}
Usage:
[Flags]
public enum Flags
{
None = 0, A = 1, B = 2, C = 4
}
Flags flags = Flags.A.SetFlag(Flags.C);
flags = flags.UnsetFlag(Flags.C);
As you can see I made use of it, that every enum implements IConvertible. So I defined extension methods on this interface which also provides a handy method for converting the value of the enum to an int. A side effect of this is that you can use this methods for every other type which implements this interface. So you could abuse this methods for something like this:
int a = 0;
a = a.SetFlag(5); // a == 5
a = a.UnsetFlag(4); // a == 1
I have a method which accepts an enum as an argument:
[Flags]
public enum MyEnum
{
A = 1,
B = 2,
C = 3
}
public class MyClass
{
public MyEnum myEnum;
}
public bool MyMethod(MyClass class, MyEnum enumParam)
{
// Here I want to check if object class contains some enum values passed as an argument
// Something like: if(class.myEnum 'contains-one-of-the-items-of enumParam)
}
public void Test()
{
Myclass class = new MyClass() { myEnum = MyEnum.A };
MyMethod(class, MyEnum.A | MyEnum.B);
}
I want to check if an object contains one of the enum values which are passed in the method as an argument.
As your using flags, this may help you with checking if an enum value has been set:
What does the [Flags] Enum Attribute mean in C#?
You can write it like this
public bool MyMethod(MyClass class, MyEnum enumParam)
{
if( (enumParam & MyEnum.A) != 0 ){
...
}
if( (enumParam & MyEnum.B) != 0 ){
...
}
}
I changed enum to enumParam to not conflict with the enum keyword.
There is also a problem with your implementation since you have the values 1,2,3 for A,B,C. This way you can't differentiate between A+B=3 and C=3. A should be 1, B should be 2 and C should be 4 (D should be 8 and so on)
EDIT
Edit due to OP's comment.
public bool MyMethod(MyClass class, MyEnum enumParam)
{
return Enum.IsDefined(typeof(MyEnum), enumParam);
}
If you want to see if any of the values passed in the parameter are in the class's myEnum field, you can write:
public bool MyMethod(MyClass class, MyEnum enum)
{
// Here I want to check if object class contains some enum values passed as an argument
// Something like: if(class.myEnum 'contains-one-of-the-items-of enum)
return (this.myEnum & enum) != 0;
}
This does a logical "AND" of the bit flags and will return true if any one of the flags in enum is set in myEnum.
If you want to ensure that all the flags are set, then you can write:
return (this.myEnum & enum) == this.myEnum;
Also, read the response by #Øyvind Bråthen carefully. In order for [Flags] to work, you need to ensure that your enum values are powers of 2.
Change your enum like this :
public enum MyEnum
{
A = 2,
B = 4,
C = 8
}
and your method is as simple as :
public bool MyMethod(MyClass aClass, MyEnum aEnum)
{
return (aClass.myEnum & aEnum) != 0;
}
Best regards
In C# 4.0 you can easily use Enum.HasFlag method. You can take a look at this question to get other solutions including C# 3.5 and previous versions.
If I have the following enum:
public enum ReturnValue{
Success = 0,
FailReason1 = 1,
FailReason2 = 2
//Etc...
}
Can I avoid casting when I return, like this:
public static int main(string[] args){
return (int)ReturnValue.Success;
}
If not, why isn't an enum value treated as an int by default?
enums are supposed to be type safe. I think they didn't make them implicitly castable to discourage other uses. Although the framework allows you to assign a constant value to them, you should reconsider your intent. If you primarily use the enum for storing constant values, consider using a static class:
public static class ReturnValue
{
public const int Success = 0;
public const int FailReason1 = 1;
public const int FailReason2 = 2;
//Etc...
}
That lets you do this.
public static int main(string[] args){
return ReturnValue.Success;
}
EDIT
When you do want to provide values to an enum is when you want to combine them. See the below example:
[Flags] // indicates bitwise operations occur on this enum
public enum DaysOfWeek : byte // byte type to limit size
{
Sunday = 1,
Monday = 2,
Tuesday = 4,
Wednesday = 8,
Thursday = 16,
Friday = 32,
Saturday = 64,
Weekend = Sunday | Saturday,
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}
This enum can then be consumed by using bitwise math. See the below example for some applications.
public static class DaysOfWeekEvaluator
{
public static bool IsWeekends(DaysOfWeek days)
{
return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
}
public static bool IsAllWeekdays(DaysOfWeek days)
{
return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
}
public static bool HasWeekdays(DaysOfWeek days)
{
return ((int) (days & DaysOfWeek.Weekdays)) > 0;
}
public static bool HasWeekendDays(DaysOfWeek days)
{
return ((int) (days & DaysOfWeek.Weekend)) > 0;
}
}
There is no implicit cast because the enum does not have to use int as the underlying type. If your enum used a uint as the underlying type, for instance, there is no implicit cast from uint to int.
The c# enum is useless.
You can avoid casting from your type AND constrain the values that can be explicitly cast to your type by making a sealed class, and providing implicit/explicit conversion operators.
Provide an implicit operator for converting from your type to a generic int so you don't have to cast.
Provide an explicit operator for converting from an int to your type, which throws an error if the integer fails to meet the constraint, such as (int x) => (x >= 0 && x <= 2).
If using this technique, create a generic immutable base class such as ConstrainedNumber<T>, which has a constructor that accepts a T value and delegate for the constraint: delegate bool NumberConstraint<T>(T value). The constructor should run the value through the constraint delegate, and throw an exception if it fails to meet the constraint. The base class should also take care of the implicit conversion operation to T, and should handle equality by overloading object.Equals(object) and object.GetHashCode(), defining == and != operators for the type ConstrainedNumber<T>, and implementing IEquatable<T> and IEquatable<ConstrainedNumber<T>>. I also recommend defining an copy constructor for the base class, and all derived types. Cloning can then be implemented cleanly in the base class by retrieving the copy constructor via reflection, but this is entirely optional. You can figure out the ConstrainedNumber<T> implementation yourself, unless I've already posted it on stackoverflow somewhere.
You can provide named static readonly values in your derived ConstrainedNumber, so that you can access them just like an enum.
public sealed class ReturnValue: ConstrainedNumber<int>
{
public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3);
public static readonly ReturnValue Success = new ReturnValue(0);
public static readonly ReturnValue FailReason1 = new ReturnValue(1);
public static readonly ReturnValue FailReason2 = new ReturnValue(2);
private ReturnValue( int value ): base( value, constraint ) {}
private ReturnValue( ReturnValue original ): base (original) {} //may be used to support IClonable implementation in base class
public static explicit operator ReturnValue( int value )
{
switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public.
{
case 0: return Success;
case 1: return FailReason1;
case 2: return FailReason2;
}
throw new ArgumentException( "Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value" );
}
}
You could use this technique for any constraint. For example, a class called EvenNumber may have a constraint that returns true if the given number is even. In that case, you'd just make your constructors public, and simplify your static conversion operator to just return a new EvenNumber, instead of switching to return one of the limited existing instances.
It could be used like this:
EvenNumber x = (EvenNumber)2;
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber." A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model
int z = x; //implicit conversion, no cast necessary;
Enums and ints are simply not implicitly castable as per the spec (except for the literal 0, which is allowed for comparison tests / assignments / etc). The explicit cast is all that is needed, though.
Strangely enough, this is not specific to the .NET Framework, but just to C#. As the other commenters have already pointed out, in C# this is basically a specification of the language. The same is not true in VB.NET.
Check out the MSDN reference page for Enums in VB.NET. Note that you can specify the data type of an enumeration at Enum declaration time.
That means, if you really don't want to litter your code with casts to (int), you could write your enumeration in VB.NET, declare it as an integer, then use that Enum from C#.
Remember how they told us computers would make our lives so much simpler? :)
No, you can't avoid casting; as to why there's no implicit conversion, I don't know, but there's not.
You can ascribe this behaviour to the basic intention behind creating Enumerations... to create a set of named constants that can only have specified (or default) values depending on the underlying type.
There are two separate issues to consider, as related to your question:
An Enum value cannot be treated as an int by default because then you would be able to provide any integer and there would be no compile time check to validate that the provided integer does in fact exist as a value in the Enumeration.
Casting becomes necessary since you are trying to convert from the governing type (of type YourCustomEnum which derives from the System.Enum class) to the underlying type, i.e., int or byte, etc.
Risking a Necromancer batch, I still like to post a possibility that didn't come up yet: To use a helper class (resp. struct) that converts implicitly into int and the enum type:
internal struct AutoCaster<T1, T2> {
private T1 Value1 { get; }
private T2 Value2 { get; }
public AutoCaster(T1 value1) {
Value1 = value1;
Value2 = (T2)(object)value1;
}
public AutoCaster(T2 value2) {
Value1 = (T1)(object)value2;
Value2 = value2;
}
public static implicit operator AutoCaster<T1, T2>(T2 input) {
return new AutoCaster<T1, T2>(input);
}
public static implicit operator AutoCaster<T1, T2>(T1 input) {
return new AutoCaster<T1, T2>(input);
}
public static implicit operator T1(AutoCaster<T1, T2> input) {
return input.Value1;
}
public static implicit operator T2(AutoCaster<T1, T2> input) {
return input.Value2;
}
}
As the Main needs a fix return type (int or void) it does not look that elegant in your example, but for other purposes it works just fine:
public static int Main(string[] args) {
return Main2(args);
}
private static AutoCaster<int, ReturnValue> Main2(string[] args) {
return ReturnValue.FailReason2;
}
How about using static Members of a Class?
//enum DocInfos { DocName, DocNumber, DocVersion};
public class DocInfos
{
public static int DocName = 0;
public static int DocNumer = 1;
public static int DocVersion = 2;
}
...
Doc = new string[DocInfos.DocVersion];
// Treffer
Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2])
...
What I want to do is something like this: I have enums with combined flagged values.
public static class EnumExtension
{
public static bool IsSet<T>( this T input, T matchTo )
where T:enum //the constraint I want that doesn't exist in C#3
{
return (input & matchTo) != 0;
}
}
So then I could do:
MyEnum tester = MyEnum.FlagA | MyEnum.FlagB
if( tester.IsSet( MyEnum.FlagA ) )
//act on flag a
Unfortunately, C#'s generic where constraints have no enum restriction, only class and struct. C# doesn't see enums as structs (even though they are value types) so I can't add extension types like this.
Does anyone know a workaround?
EDIT: This is now live in version 0.0.0.2 of UnconstrainedMelody.
(As requested on my blog post about enum constraints. I've included the basic facts below for the sake of a standalone answer.)
The best solution is to wait for me to include it in UnconstrainedMelody1. This is a library which takes C# code with "fake" constraints such as
where T : struct, IEnumConstraint
and turns it into
where T : struct, System.Enum
via a postbuild step.
It shouldn't be too hard to write IsSet... although catering for both Int64-based and UInt64-based flags could be the tricky part. (I smell some helper methods coming on, basically allowing me to treat any flags enum as if it had a base type of UInt64.)
What would you want the behaviour to be if you called
tester.IsSet(MyFlags.A | MyFlags.C)
? Should it check that all the specified flags are set? That would be my expectation.
I'll try to do this on the way home tonight... I'm hoping to have a quick blitz on useful enum methods to get the library up to a usable standard quickly, then relax a bit.
EDIT: I'm not sure about IsSet as a name, by the way. Options:
Includes
Contains
HasFlag (or HasFlags)
IsSet (it's certainly an option)
Thoughts welcome. I'm sure it'll be a while before anything's set in stone anyway...
1 or submit it as a patch, of course...
As of C# 7.3, there is now a built-in way to add enum constraints:
public class UsingEnum<T> where T : System.Enum { }
source: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
Darren, that would work if the types were specific enumerations - for general enumerations to work you have to cast them to ints (or more likely uint) to do the boolean math:
public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
As of C# 7.3, you can use the Enum constraint on generic types:
public static TEnum Parse<TEnum>(string value) where TEnum : Enum
{
return (TEnum) Enum.Parse(typeof(TEnum), value);
}
If you want to use a Nullable enum, you must leave the orginial struct constraint:
public static TEnum? TryParse<TEnum>(string value) where TEnum : struct, Enum
{
if( Enum.TryParse(value, out TEnum res) )
return res;
else
return null;
}
Actually, it is possible, with an ugly trick.
However, it cannot be used for extension methods.
public abstract class Enums<Temp> where Temp : class {
public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
return (TEnum)Enum.Parse(typeof(TEnum), name);
}
}
public abstract class Enums : Enums<Enum> { }
Enums.IsSet<DateTimeKind>("Local")
If you want to, you can give Enums<Temp> a private constructor and a public nested abstract inherited class with Temp as Enum, to prevent inherited versions for non-enums.
You can achieve this using IL Weaving and ExtraConstraints
Allows you to write this code
public class Sample
{
public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{
}
public void MethodWithEnumConstraint<[EnumConstraint] T>()
{
}
}
What gets compiled
public class Sample
{
public void MethodWithDelegateConstraint<T>() where T: Delegate
{
}
public void MethodWithEnumConstraint<T>() where T: struct, Enum
{
}
}
This doesn't answer the original question, but there is now a method in .NET 4 called Enum.HasFlag which does what you are trying to do in your example
The way I do it is put a struct constraint, then check that T is an enum at runtime. This doesn't eliminate the problem completely, but it does reduce it somewhat
Using your original code, inside the method you can also use reflection to test that T is an enum:
public static class EnumExtension
{
public static bool IsSet<T>( this T input, T matchTo )
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Must be an enum", "input");
}
return (input & matchTo) != 0;
}
}
Here's some code that I just did up that seems to work like you want without having to do anything too crazy. It's not restricted to only enums set as Flags, but there could always be a check put in if need be.
public static class EnumExtensions
{
public static bool ContainsFlag(this Enum source, Enum flag)
{
var sourceValue = ToUInt64(source);
var flagValue = ToUInt64(flag);
return (sourceValue & flagValue) == flagValue;
}
public static bool ContainsAnyFlag(this Enum source, params Enum[] flags)
{
var sourceValue = ToUInt64(source);
foreach (var flag in flags)
{
var flagValue = ToUInt64(flag);
if ((sourceValue & flagValue) == flagValue)
{
return true;
}
}
return false;
}
// found in the Enum class as an internal method
private static ulong ToUInt64(object value)
{
switch (Convert.GetTypeCode(value))
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture);
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
}
throw new InvalidOperationException("Unknown enum type.");
}
}
if someone needs generic IsSet (created out of box on fly could be improved on), and or string to Enum onfly conversion (which uses EnumConstraint presented below):
public class TestClass
{ }
public struct TestStruct
{ }
public enum TestEnum
{
e1,
e2,
e3
}
public static class TestEnumConstraintExtenssion
{
public static bool IsSet<TEnum>(this TEnum _this, TEnum flag)
where TEnum : struct
{
return (((uint)Convert.ChangeType(_this, typeof(uint))) & ((uint)Convert.ChangeType(flag, typeof(uint)))) == ((uint)Convert.ChangeType(flag, typeof(uint)));
}
//public static TestClass ToTestClass(this string _this)
//{
// // #generates compile error (so no missuse)
// return EnumConstraint.TryParse<TestClass>(_this);
//}
//public static TestStruct ToTestStruct(this string _this)
//{
// // #generates compile error (so no missuse)
// return EnumConstraint.TryParse<TestStruct>(_this);
//}
public static TestEnum ToTestEnum(this string _this)
{
// #enum type works just fine (coding constraint to Enum type)
return EnumConstraint.TryParse<TestEnum>(_this);
}
public static void TestAll()
{
TestEnum t1 = "e3".ToTestEnum();
TestEnum t2 = "e2".ToTestEnum();
TestEnum t3 = "non existing".ToTestEnum(); // default(TestEnum) for non existing
bool b1 = t3.IsSet(TestEnum.e1); // you can ommit type
bool b2 = t3.IsSet<TestEnum>(TestEnum.e2); // you can specify explicite type
TestStruct t;
// #generates compile error (so no missuse)
//bool b3 = t.IsSet<TestEnum>(TestEnum.e1);
}
}
If someone still needs example hot to create Enum coding constraint:
using System;
/// <summary>
/// would be same as EnumConstraint_T<Enum>Parse<EnumType>("Normal"),
/// but writen like this it abuses constrain inheritence on System.Enum.
/// </summary>
public class EnumConstraint : EnumConstraint_T<Enum>
{
}
/// <summary>
/// provides ability to constrain TEnum to System.Enum abusing constrain inheritence
/// </summary>
/// <typeparam name="TClass">should be System.Enum</typeparam>
public abstract class EnumConstraint_T<TClass>
where TClass : class
{
public static TEnum Parse<TEnum>(string value)
where TEnum : TClass
{
return (TEnum)Enum.Parse(typeof(TEnum), value);
}
public static bool TryParse<TEnum>(string value, out TEnum evalue)
where TEnum : struct, TClass // struct is required to ignore non nullable type error
{
evalue = default(TEnum);
return Enum.TryParse<TEnum>(value, out evalue);
}
public static TEnum TryParse<TEnum>(string value, TEnum defaultValue = default(TEnum))
where TEnum : struct, TClass // struct is required to ignore non nullable type error
{
Enum.TryParse<TEnum>(value, out defaultValue);
return defaultValue;
}
public static TEnum Parse<TEnum>(string value, TEnum defaultValue = default(TEnum))
where TEnum : struct, TClass // struct is required to ignore non nullable type error
{
TEnum result;
if (Enum.TryParse<TEnum>(value, out result))
return result;
return defaultValue;
}
public static TEnum Parse<TEnum>(ushort value)
{
return (TEnum)(object)value;
}
public static sbyte to_i1<TEnum>(TEnum value)
{
return (sbyte)(object)Convert.ChangeType(value, typeof(sbyte));
}
public static byte to_u1<TEnum>(TEnum value)
{
return (byte)(object)Convert.ChangeType(value, typeof(byte));
}
public static short to_i2<TEnum>(TEnum value)
{
return (short)(object)Convert.ChangeType(value, typeof(short));
}
public static ushort to_u2<TEnum>(TEnum value)
{
return (ushort)(object)Convert.ChangeType(value, typeof(ushort));
}
public static int to_i4<TEnum>(TEnum value)
{
return (int)(object)Convert.ChangeType(value, typeof(int));
}
public static uint to_u4<TEnum>(TEnum value)
{
return (uint)(object)Convert.ChangeType(value, typeof(uint));
}
}
hope this helps someone.
I just wanted to add Enum as a generic constraint.
While this is just for a tiny helper method using ExtraConstraints is a bit too much overhead for me.
I decided to just just create a struct constraint and add a runtime check for IsEnum. For converting a variable from T to Enum I cast it to object first.
public static Converter<T, string> CreateConverter<T>() where T : struct
{
if (!typeof(T).IsEnum) throw new ArgumentException("Given Type is not an Enum");
return new Converter<T, string>(x => ((Enum)(object)x).GetEnumDescription());
}