96

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?

Ijas Ameenudeen
  • 9,069
  • 3
  • 41
  • 54
Keith
  • 150,284
  • 78
  • 298
  • 434
  • 2
    Keith: download version 0.0.0.2 of UnconstrainedMelody - I've implemented HasAll and HasAny. Enjoy. – Jon Skeet Sep 11 '09 at 21:22
  • What do you mean by “C# doesn't see enums as structs”? You can use enum types as type parameters that are constrained to `struct` just fine. – Timwi Sep 25 '10 at 19:38
  • check this article here: http://www.codeproject.com/KB/cs/ExtendEnum.aspx 'IsValidEnumValue' or 'IsFlagsEnumDefined' methods are probably the answer to your question. – dmihailescu Jul 01 '11 at 14:44
  • @dmihailescu - see @Jon Skeet's far more complete and detailed solution in the accepted answer. – Keith Jul 04 '11 at 08:44
  • @dmihailescu: No, that code project is addressing a different, more complicated problem: determining whether a given value is a **valid** value for a given Enum. E.g. if you are handed a value "9", is that valid for your Enum? (No if you only have 3 flag bits). OP is trying to generalize simple bit operations on a Flags enum. These operations are one-liners, if you have a SPECIFIC Enum type. E.g. clearing a Flag bit is something like `value = value & (~MyEnum.FlagA)`. But its a pain having to write that each time, so it would be nice to write generic methods once, and be done with it. – ToolmakerSteve Mar 19 '14 at 06:43
  • 11
    [C# 7.3](https://learn.microsoft.com/en-us/visualstudio/releasenotes/vs2017-preview-relnotes#csharp) introduces enum constraints. – Marc Sigrist Apr 10 '18 at 12:02

12 Answers12

52

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...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I suppose if multiple flags are passed in it should check for all of them. My actual fix for this (back in 2008 when I asked it) was to have a template extension method for each flags enum - messy but works. Never bothered with the check for multiple flags because all the checks we have are for a single flag - not such a problem in internal only code but something that would need to be accounted for in an shared library. – Keith Sep 11 '09 at 09:32
  • 1
    You had to go and mention PostSharp LOL :o http://www.postsharp.org/blog/generic-constraints-for-enums-and-delegates – Sam Harwell Sep 11 '09 at 09:34
  • I would use the Flags-terminology simply because it already exists in .NET (see `FlagsAttribute`.) I see two explicit names here: `HasAnyFlag` and `HasAllFlags`. They can be shortened to `HasFlag` and `HasFlags`. I can't say which is best, it's a matter of taste I guess. – Blixt Sep 11 '09 at 10:19
  • Oh and Keith, have a look at http://stackoverflow.com/questions/1404077/is-there-a-workaround-for-generic-type-constraint-of-special-class-enum-in-c-3 `=)` – Blixt Sep 11 '09 at 10:21
  • 1
    Or actually simpler HasAny() and HasAll() – Keith Sep 11 '09 at 10:39
  • 1
    Yes, I agree that's even better. `colors.HasAny(Colors.Red | Colors.Blue)` looks like very readable code. `=)` – Blixt Sep 11 '09 at 10:49
  • 1
    Yup, I like HasAny and HasAll too. Will go with that. – Jon Skeet Sep 11 '09 at 10:56
  • 1
    HasAny and HasAll seem awesome. – IDisposable Sep 15 '09 at 23:29
  • As I started reading the top of the Answer, I started saying to myself "Another library? Screw that, unless it was written by Jon Skeet." Then I scrolled down, and was rewarded. – krillgar Sep 26 '16 at 20:51
  • 6
    Since C# 7.3 (released May 2018), it is possible to use the constraint `where T : System.Enum`. This was already written elsewhere in the thread; just thought I would repeat it here. – Jeppe Stig Nielsen May 29 '18 at 10:00
37

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

Ivan Ferić
  • 4,725
  • 11
  • 37
  • 47
  • Do consider `where T : struct, System.Enum`. – Jeppe Stig Nielsen Jun 11 '22 at 11:33
  • @JeppeStigNielsen, while it will work like that as well, it's not adding any value. All enums (and only enums) implement `System.Enum`. All enums (but not only enums) are also `struct`. Adding both will cover only enums. Using only `System.Enum` will do that as well... – Ivan Ferić Jun 13 '22 at 07:27
  • 1
    It only makes a difference because `System.Enum` **itself** is a reference type which also meets your version of the constraint, but not mine. This makes a difference with nullable support, among other things. For example, if you use `T?` somewhere in your class, let us say a property `public T? Value { get; set; }` then it makes a big difference if the compiler knows whether it is a value type or not. Also without any `?`, if you do `public bool IsNull(T t) => t == null;`, with my version of the constraint the compiler can emit a warning that `t` is never null. – Jeppe Stig Nielsen Jun 13 '22 at 07:59
  • It really depends on how you want to use this class. In most real-case scenarios, you wouldn't have a need for `struct`. With `public T? Value { get; set; }`, if you're using nullable reference types, the only actual difference would be that you would need to compare `Value` to null instead of using `.HasValue`. As for `public bool IsNull(T t) => t == null;`, why would you even have it if you're working with enums? It would only make sense to use `public bool IsNull(T? t) => t == null;` – Ivan Ferić Jun 13 '22 at 11:04
  • Well, that's what I'm saying. It depends on what you want out of it. The question is not asking for nullable enums and only that need would require an additional `struct` constraint. The question as it stands doesn't need it. – Ivan Ferić Jun 13 '22 at 15:32
17

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;
}
Ronnie
  • 8,053
  • 6
  • 34
  • 34
  • 1
    And if you have a ridiculous number of flags, you can call GetTypeCode() on the arguments and Convert.ToUint64() – Kit Sep 10 '09 at 02:42
  • Awesome, the combination of 'Enum` and `Convert.ToUInt32` I didn't find anywhere else. AFAIK, Its the only decent Pre-Net-4 solution that also works in VB. BTW, if `matchTo` might have multiple flag bits, then replace `!= 0` with `== Convert.ToUInt32(matchTo)`. – ToolmakerSteve Mar 19 '14 at 06:56
  • 1
    Note that `Convert.ToUInt32` used with an enum will use the `Convert.ToUInt32(object)` overload, meaning that CLR will first box these values before passing then to the `ToUInt32` method. In most cases this won't matter, but it's good to know that you'll keep the GC rather busy if you're using something like this to parse millions of enums per second. – vgru Dec 18 '14 at 15:08
11

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;
}
Mik
  • 1,306
  • 1
  • 10
  • 14
10

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.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
8

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
    {
    }
}
Simon
  • 33,714
  • 21
  • 133
  • 202
4

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

Phil Devaney
  • 17,607
  • 6
  • 41
  • 33
  • Upvoted because at this point, most everyone should be using .NET 4 (or higher) and so they should be using this method instead of trying to hack it together. – CptRobby Nov 20 '14 at 20:01
  • Upvoted. However their solution uses boxing of the argument `flag`. .NET 4.0 is five years old now. – Jeppe Stig Nielsen Apr 19 '15 at 08:57
3

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

thecoop
  • 45,220
  • 19
  • 132
  • 189
  • 7
    where T : struct, IComparable, IFormattable, IConvertible -- this is the closest you can get to enum :) – Kit Sep 10 '09 at 02:39
1

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;
    }
}
Keith
  • 150,284
  • 78
  • 298
  • 434
Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
  • 2
    Thanks, but that turns a compile time issue (the where constraint) into a runtime one (your exception). Also you'd still need to convert the inputs to ints before you could do anything with them. – Keith Sep 11 '09 at 11:13
1

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.");
    }
}
Brian Surowiec
  • 17,123
  • 8
  • 41
  • 64
0

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());
    }
BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
0

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&lt;Enum>Parse&lt;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.

SoLaR
  • 778
  • 7
  • 22