tags:

views:

101

answers:

6

I have an object that represents a physical structure (like a utility pole), and it touches a bunch of other objects (the wires on the pole). The other objects have a bunch of characteristics (status, size, voltage, phase, etc.) expressed as enums. I want to write a generic function that counts how many of the wires match any or all of the characteristics.

If enums were first-class objects, I think I'd just write this:

CountWires(EStatus status, ESize size, EVoltage voltage, EPhase phase)
{
    int count = 0;
    foreach (Wire wire in _connectedWires)
    {
        if (status != null && wire.Status != status) continue;
        if (size != null && wire.Size != size) continue;
        ...
        ++count;
    }
    return count;
}

... and be able to call it to count just new, large wires of any voltage and phase like this:

CountWires(EStatus.New, ESize.Large, null, null);

... but of course that gets me a cannot convert from '<null>' to 'ESize' error.

We've tackled this in the past by adding an "Any" value to the enums themselves and checking against that, but then if we do something like display all the possible values in a list for the user we have to filter out "Any". So I want to avoid that.

I thought I'd throw this out to the community and see if anyone has any ideas for a way to do this with a clean interface and easy-to-read calling code. I have my own answer I'm toying with that I'll add to the discussion.

A: 

I'm experimenting with creating a NullableEnum class:

class NullableEnum<T> where T : struct
{
    T _value;
    NullableEnum(T value) { _value = value; }
    public T Value { get { return _value; } }
}

... that does nothing but hold a value and be a first-class object. Then I'd have this signature:

CountWires(NullableEnum<EStatus> status,
           NullableEnum<ESize> size,
           NullableEnum<EVoltage> voltage,
           NullableEnum<EPhase> phase)

...but then the calling code has to look like this:

CountWires(NullableEnum<EStatus>(EStatus.New), NullableEnum<ESize>(ESize.Large), null, null);

...which is a bit cumbersome.

Chuck Wilbur
You're reinventing the wheel... the `Nullable<T>` structure exists, so you just have to use `Nullable<EStatus>`, or `EStatus?` for short. (see Guvante's answer)
Thomas Levesque
+4  A: 

Have you tried a normal nullable? With the ? syntax it is pretty concise (And C# has built in implicit operators for it)

CountWires(EStatus? status, ESize? size, EVoltage? voltage, EPhase? phase)

And the use is pretty easy

if (status.HasValue)
  stats.Value
Guvante
+2  A: 

Can't you just use

CountWires(EStatus? status, ESize? size, EVoltage? voltage, EPhase? phase)

You just have to do something like this at the start of the CountWires function to ensure status is not null when your code runs

status = status ?? EStatus.DefaultValue;

Or whatever value your code needs to run ... in your case, you actually don't want defaults, but to leave them as null

Chad
+2  A: 

How about

CountWires(EStatus? status, ESize? size, EVoltage? voltage, EPhase? phase)
Bryce Fischer
hehe. everyone posted the same thing, within seconds :-)
Bryce Fischer
+9  A: 

Or just use nullable types.

int CountWires(EStatus? status, ESize? size, EVoltage? voltage, EPhase? phase)
{
    int count = 0;
    foreach (Wire wire in _connectedWires)
    {
        if (status.HasValue && wire.Status.Value != status) continue;
        if (size.HasValue && wire.Size.Value != size) continue;
        ...
        ++count;
    }
    return count;
}
Robert Davis
Bonus points and accepted for modifying my own code sample. Very nice.I really should take a formal C# course one of these days just to learn all these handy little operators.
Chuck Wilbur
A: 

Along the same lines, Mr. Jon Skeet has created a nifty type-constrained enum library called Unconstrained Melody.

Jesse C. Slicer