1

When I declare an enum I often add a NOT_SET = 0 value to know when instances have not been initialized. When I enumerate the values of such an enum (as described here and here), what is the best way to discard that NOT_SET value? The best I could come up with was

Enum.GetValues(typeof(T)).Cast<int>().Where(item => (0 != (int)item)).Cast<T>();

I don't like it because there is too much iterating with all those Cast<>'s. I would prefer something based on

(T[]) Enum.GetValues(typeof(T))

without all the Cast<>'s.

Community
  • 1
  • 1
Jimmy
  • 4,610
  • 8
  • 51
  • 71

3 Answers3

5

It's not clear why you've got two Cast<T> calls anyway, but it's easy enough to do in two steps:

var allValues = (T[]) Enum.GetValues(typeof(T));
var allButDefault = allValues.Except(Enumerable.Repeat(default(T), 1));

Using Except here avoids boxing, assuming the default comparer does the right thing. Mind you, it will require a set to be built, so you may be better off with my original code:

var allValues = (T[]) Enum.GetValues(typeof(T));
var allButDefault = allValues.Where(t => !t.Equals(default(T)));

Or use the default equality comparer for T:

var allValues = (T[]) Enum.GetValues(typeof(T));
var comparer = EqualityComparer<T>.Default;
var allButDefault = allValues.Where(t => !comparer.Equals(t, default(T)));

Note that all of these will handle non-int-based enums too.

You might also want to look at my Unconstrained Melody project which builds on the idea of actually constraining T to be an enum.

There are various options which you can use for more performance, avoiding creating an array every time etc. We don't know how performance-critical this code is, so I haven't gone into the details here.

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
1

How do you differentiate between these sorts of cases:

  • An enum with a declared unknown or not set label for its default(T):
    public enum Foo { Unknown = 0 , Alpha = 1 , Bravo = 2 , Charlie = 3 , }

  • An enum with no label defined for its default(T):
    `public enum Bar { Alpha = 1 , Bravo = 2 , Charlie = 3 , }

  • An enum with a label defined for its default(T), but that label represents an actual valid value?
    public enum FooBar { Alpha = 0 , Bravo = 1, Charlie = 2 , }

  • A [Flags] enum where no bits set has meaning:
    [Flags] public enum ErrorConditions { None = 0x0000 , ErrorState_X = 0x0001 , ErrorState_Y = 0x0002 , ErrorState_Z = 0x0004 , ErrorState_XY = 0x0003 , ... }

It seems to me that without knowing the underlying semantics of the enum in question, you can't do what you want to do without eventually shooting yourself in the foot.

Nicholas Carey
  • 60,260
  • 12
  • 84
  • 126
1

I don't like it because there is too much iterating with all those Cast<>'s.

Your statement indicates that you misunderstand what's happening. You'll find, if you download the source and enable .NET Framework source stepping, that "all that iterating" you mention simply isn't happening. LINQ turns your expression into a single loop.

Granted, it's a somewhat more complicated loop than what you might write yourself, but it certainly isn't creating a collection of integers, then creating a collection of enums.

Jim Mischel
  • 122,159
  • 16
  • 161
  • 305