9

First things off, I had no idea what to title this question - I'm even confused how to state it.

Now for the question. Let's take the System.IO.FileSystemWatcher class where you set it's NotifyFilter property:

            this.FileSystemWatcher1.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.FileName 
            | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security 
            | NotifyFilters.Size;

That's quite a bit of code to set a single property. Inspecting NotifyFilter, it is an enumeration. Is there a 'lazy' or 'shortcut' way to set all these properties at once? I know it's not necessarily needed, but my curiosity is piqued.

this.FileSystemWatcher1.NotifyFilter = <NotifyFilters.All> ?

Eon
  • 3,833
  • 10
  • 46
  • 75
  • I prefer to define this sort of thing explicitly, e.g. in a field called All. You could then exclude specific enum values by using `~`. – GregRos Jul 20 '14 at 21:07
  • @GregRos Can you somehow demonstrate what you mean? – Eon Jul 20 '14 at 21:10

4 Answers4

13

You could always do something like this,

NotifyFilter ret = 0;
foreach(NotifyFilter v in Enum.GetValues(typeof(NotifyFilter)))
{
    ret |= v;   
}

I don't know of a better way, unfortunately. But you could always throw that in a generic utility method.

private static T GetAll<T>() where T : struct, IConvertible
{
    if (!typeof(T).IsEnum)
    {
       throw new NotSupportedException(); // You'd want something better here, of course.
    }

    long ret = 0; // you could determine the type with reflection, but it might be easier just to use multiple methods, depending on how often you tend to use it.
    foreach(long v in Enum.GetValues(typeof(T)))
    {
        ret |= v;
    }

    return (T)ret;
}
Matthew Haugen
  • 12,916
  • 5
  • 38
  • 54
  • 2
    Just an FYI, you could say `foreach(NotifyFilter in Enum.GetValues(typeof(NotifyFilter)))` saves the cast inside the foreach body. – Lukazoid Jul 20 '14 at 21:05
  • Good one. I am working on some major personal projects, and would like to keep my application fast. Didn't occur to me to use generic methods to nail this problem. – Eon Jul 20 '14 at 21:09
  • Constraint your `T` to `where T: struct, IConvertible` and check `if (!typeof(T).IsEnum) { throw ... }` – Yuval Itzchakov Jul 20 '14 at 21:11
  • 1
    `(T)ret` doesn't compile for me -- but I'm stuck on c# 3.0. Does it work in 4.0/4.5? – dbc Jul 20 '14 at 21:19
  • @dbc That is disallowed in any version of C#. There is no conversion from `int` to a type argument `T` (no matter what constraints `T` carries). – Jeppe Stig Nielsen Jul 20 '14 at 21:38
  • If you assume that the underlying type of the enum `T` is `int`, maybe you should check that where you check `typeof(T).IsEnum`. Otherwise you will get unexpected behavior if some enum uses `long` as its underlying type. – Jeppe Stig Nielsen Jul 20 '14 at 21:41
  • 1
    @Noobgrammer Maybe it is OK to set all flags, even those not used or named. In that case you can just do `this.FileSystemWatcher1.NotifyFilter = (NotifyFilters)(~0);` or (equivalently) `this.FileSystemWatcher1.NotifyFilter = (NotifyFilters)(-1);`. But be sure to check if the underlying type of `NotifyFilters` is `int` ([it is](http://msdn.microsoft.com/en-us/library/system.io.notifyfilters.aspx)). – Jeppe Stig Nielsen Jul 20 '14 at 21:48
  • @JeppeStigNielsen thanks, good point. I realized that as I had to leave. I was going to add in the support for `long`, but I figured this gives the gist to people so I left it up. – Matthew Haugen Jul 20 '14 at 22:43
  • I assume `ret |= (v;` is a typo? – Blorgbeard Jul 20 '14 at 22:49
  • @Blorgbeard that's what I get for answering in a hurry. Thanks for pointing that out! Fixed. – Matthew Haugen Jul 20 '14 at 22:50
6

There isn't a way without writing your own method - as correctly answered elsewhere.

If the enum was yours to change you could add a new value

All = ~0
ajg
  • 1,743
  • 12
  • 14
3

If the enumeration has the Flags attribute applied on it you can add the numbers equivalent to the desired enum members and then assign it.

In the example in your question sum up 4+64+32+16+8 = 124 then you can write

this.FileSystemWatcher1.NotifyFilter = (NotifyFilters) 124;

Of course this takes away the readability of an enum and dramatically causes maintenance issues, but as you said it's for our lazies:

public class Program
{
    [Flags]
    enum Enum:int
    {
        a = 1,
        b = 2
    }

    static void Main(string[] args)
    {
        Enum x = (Enum) 3;
        Console.WriteLine(x.ToString());

    }
}

Output:

a,b

UPDATE:

I almost forgot. You can of course pass this string to Enum.Parse and get the desired value:

this.FileSystemWatcher1.NotifyFilter = (NotifyFilter) Enum.Parse(typeof(NotifyFilter), 
"Attributes,CreationTime,FileName,LastAccess,LastWrite,Security,Size"); 
Alireza
  • 4,976
  • 1
  • 23
  • 36
3

Writing a generic utility to mask together all the values in a c# enum turns out to be much more difficult than one would imagine, because

  1. The underlying type of an enum can be byte, short, int or long; signed or unsigned.
  2. There is no way to cast an object directly to a generic enum, perhaps because there's no enum constraint built into c#. One must box instead, then unbox.
  3. All the enum utilities date from c# 1.0 and so are rather crufty.

This is the best I could do. I made use of the following:

  1. Cast Int to Generic Enum in C#
  2. Enum type constraints in C#

Would this be easier in c++/CLI?

/// <summary>
/// Contains generic utilities for enums, constrained for enums only.
/// </summary>
public sealed class EnumHelper : Enums<Enum>
{
    private EnumHelper()
    {
    }
}

/// <summary>
/// For use by EnumHelper, not for direct use.
/// </summary>
public abstract class Enums<TEnumBase> where TEnumBase : class, IConvertible
{
    private static void ThrowOnEnumWithoutFlags<TEnum>() where TEnum : struct, TEnumBase
    {
        var attributes = typeof(TEnum).GetCustomAttributes(typeof(FlagsAttribute), false);
        if (attributes.Length == 0)
        {
            throw (new ArgumentException("The generic argument [<T>] must be an enumeration with the [FlagsAttribute] applied.", "T: " + typeof(TEnum).FullName));
        }
    }

    public static TEnum GetAll<TEnum>() where TEnum : struct, TEnumBase
    {
        ThrowOnEnumWithoutFlags<TEnum>();
        var underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
        if (underlyingType == typeof(ulong))
        {
            ulong value = 0;
            foreach (var v in Enum.GetValues(typeof(TEnum)))
                // Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
                value |= Convert.ToUInt64(v, CultureInfo.InvariantCulture); 
            return (TEnum)Enum.ToObject(typeof(TEnum), value);
        }
        else
        {
            long value = 0;
            foreach (var v in Enum.GetValues(typeof(TEnum)))
                // Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
                value |= Convert.ToInt64(v, CultureInfo.InvariantCulture);
            return (TEnum)Enum.ToObject(typeof(TEnum), value);
        }
    }

I have tested on byte, sbyte, short, int, long, ushort, uint and ulong types.

Update simplified as per hvd's suggestion.

Community
  • 1
  • 1
dbc
  • 104,963
  • 20
  • 228
  • 340
  • 1
    +1 You do need separate handlers for `long` and `ulong` to handle values that are out of `long`'s range, but for all other types, you can use the `long` version: simply use `Convert.ToInt64` and `Enum.ToObject` to perform the conversions. (And for consistency, you can use `Convert.ToUInt64` and `Enum.ToObject` for the `ulong` version.) –  Jul 21 '14 at 11:06