5
\$\begingroup\$

I have an enum like the following:

enum MeasurementBandwidth
{
    Hz1 = 1,
    Hz3 = 3,
    Hz10 = 10,
    ...
}

But I do not like the Hz1, and 1Hz is not valid as it starts with a number.

Does anyone have an idea how to solve this "more elegant"?

Why I am doing this:

I have an API, which offers a method like the following:

public double MeasureInput(double frequency, MeasurementBandwidth bandwidth) { /*...*/ }

And the bandwidth only has a few possible values which can be used and I want to make it as easy as possible for the "user" to choose a valid one. Using an enum was the best solution I came up with, as it is directly visible from the parameter type, what values are valid.

\$\endgroup\$

closed as off-topic by Jamal Nov 2 '15 at 20:33

This question appears to be off-topic. The users who voted to close gave this specific reason:

If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    \$\begingroup\$ I'm not sure this should be an enum in the first place. \$\endgroup\$ – CodesInChaos Oct 18 '13 at 13:42
  • 1
    \$\begingroup\$ I am open to alternatives: The bandwidth has several possible values which are allowed and is not a "freely definable number" and I thought an enum is the easiest way to "limit that"... \$\endgroup\$ – ChrFin Oct 18 '13 at 13:45
  • 3
    \$\begingroup\$ You still need to validate it, an enum can have unnamed values, its default value is 0. Personally I'd go with an integer complemented by helper functions that check if a particular frequency is valid and that list valid frequencies. \$\endgroup\$ – CodesInChaos Oct 18 '13 at 13:48
  • \$\begingroup\$ It's also possible that which values are valid depends on the context (perhaps v2 supports different values) \$\endgroup\$ – CodesInChaos Oct 18 '13 at 13:49
  • 1
    \$\begingroup\$ I think you want something like a Dictionary. and then use something on the user interface to change the value if need be so the number is in front of the Hz \$\endgroup\$ – Malachi Oct 18 '13 at 14:23
12
\$\begingroup\$
public enum Hertz{
    none = 0,
    Hz= 1,
    KHz = 1000, 
    MHz = 1000000,
    GHz = 1000000000
}

public static class Frequency{
    public static int Hz(this int frequency){
        return frequency;
    } 

    public static int KHz(this int frequency) {
        return  frequency * (int)Hertz.KHz;
    }

}

client code:

int freq = 10.Hz();
int faster = 10.KHz();
int thousandsOfGigahertz = 234.KHz().GHz();
\$\endgroup\$
  • 2
    \$\begingroup\$ this has got to be the coolest thing i have seeing. I think I want to do something like this when I'm talking about capacity in bytes. :) \$\endgroup\$ – Robert Snyder Oct 20 '13 at 20:32
  • 3
    \$\begingroup\$ It's clever, but I'd have some maintanability concerns. It will pollute the namespace with more extension methods (coming up in IntelliSense for every int, no matter if it's relevant). It could be confusing (what will Hertz.MHz.KHz() return?). It operates on primitives (ints), so there is no type safety - nothing to prevent performing the same conversion twice. It's an interesting and instructive approach, but depending on the context I might be tempted to say "cool, but no thanks" in a real-life peer review. \$\endgroup\$ – Konrad Morawski Nov 2 '15 at 17:05
  • 1
    \$\begingroup\$ It could be improved, perhaps by introducing a full-fledged Frequency class, aware of its unit type. Although this would no longer be a light-weight solution. \$\endgroup\$ – Konrad Morawski Nov 2 '15 at 17:05
  • \$\begingroup\$ @KonradMorawski, roger on the intelliSense point. In the past I've put the extension class by itself in a sub-namespace within my "target" namespace. \$\endgroup\$ – radarbob Nov 2 '15 at 19:15
4
\$\begingroup\$

if you really want it to be 1Hz you could put an Underscore(_) in front of the variable name. I know that I like to use an underscore(_) in front of the variable to denote a private variable. but that might not be the norm anymore. there is no way to make the first character of a variable name a number, it is forbidden in .NET, I say this because I think that it is true even in VB.

I Don't think that it would be more elegant to do it this way.

using the Hz in the beginning would help if you were going to also create variables like mHz or MHz (whatever that notation is) because then you could grab the variables by type a lot easier, you could create statements that decide what to do if the name is Hz or kHz or MHz or GHz or whatever.

I also Agree with CodesInChaos, in that this shouldn't be an enumeration.

After Reading the Update

I think what you really want to do is something like a Dictionary or something similar, so you have the value you want the user to see and the value you want to provide to the code.

\$\endgroup\$
  • \$\begingroup\$ I actually also do have kHz1 as an option, but underscores are "forbidden" by our coding convention (and I personally also do not like them), but thanks for the idea... \$\endgroup\$ – ChrFin Oct 18 '13 at 13:52
  • \$\begingroup\$ that is the only way that I can think of to make the number appear as the "first" character in a variable name. \$\endgroup\$ – Malachi Oct 18 '13 at 13:56
  • \$\begingroup\$ I know that, thanks. Maybe my question is not very clear: I am searching for other ideas, how to "solve this problem", because I know that it is not allowed to start with a number... \$\endgroup\$ – ChrFin Oct 18 '13 at 13:57
2
\$\begingroup\$

There, I'll say it: it can't be done.

C# doesn't allow identifiers to start with digits.

All workarounds either won't meet all your requirements (eg. @radarbob's answer, while clever, doesn't let you narrow down numeric values to a predefined set of legal ones, which I understand to be your main goal) - or will get you on a ride that ends up back on square one (if you use a dictionary as @Malachi suggests, then the name of this dictionary will go upfront, and it still doesn't start with a digit).

Underscore prefixes are as close as you can get.

Abusing Unicode a bit might work to a degree:

public enum Frequency
{
    ᒿHz
}

It compiles, since ᒿ != 2. But it won't work for every digit - see https://github.com/reinderien/mimic/wiki/Character-Set if you want to try.

Even if it did, I wouldn't recommend doing this in production code :)

All in all it's a fun thought exercise, trying to find a workaround and kind of trick C# into doing something it's designed not to. But the truly elegant way is to embrace the language the way it is, and not try to hammer square pegs into round holes.

If you were hell-bent on using extension methods, at the cost of being unable to rule out illegal values in compile-time, here's make take on it - more heavy-weight, but providing type and conversion safety. I did it for the heck of it, I know it's not the perfect solution.

It resembles @radarbob's answer in that it puts extension methods to use.

Let's define the basics (it can all go into one static class):

    public enum Unit
    {
        Hz,
        KHz,
        GHz
    }

    private static readonly Dictionary<Unit, int> scale = new Dictionary<Unit, int>()
    {
        { Unit.Hz, 1 },
        { Unit.KHz, 1000 },
        { Unit.GHz, 1000 * 1000 }
    };

We're kind of emulating Java's enums here (one of few features where Java actually happens to be superior to C#).

And a little bit of help (so that we don't use reflection etc.):

    private delegate Frequency FrequencyProvider(int rawValue);
    private static readonly Dictionary<Unit, FrequencyProvider> providers = new Dictionary<Unit, FrequencyProvider>
    {
        { Unit.Hz, f => new Herz(f) },
        { Unit.KHz, f => new KiloHerz(f) },
        { Unit.GHz, f => new GigaHerz(f) }
    };

Now the base class will look as follows:

    public abstract class Frequency
    {
        private int rawValue;
        private readonly int unitScale;

        protected Frequency()
        {
            this.unitScale = scale[GetUnit()];
        }

        public Frequency(int rawValue)
        {
            this.rawValue = rawValue;
        }

        protected abstract Unit GetUnit();

        public int Value
        {
            get
            {
                return rawValue / unitScale;
            }
            set
            {
                rawValue = value * unitScale;
            }
        }

        private Frequency To(Unit unit)
        {
            Frequency converted = providers[unit](this.rawValue);                
            return converted;
        }

        public Frequency ToHz()
        {
            return To(Unit.Hz);
        }

        public Frequency ToKHz()
        {
            return To(Unit.KHz);
        }

        public Frequency ToGHz()
        {
            return To(Unit.GHz);
        }
    }

And its child classes:

    public class Herz : Frequency
    {
        public Herz(int rawValue) : base(rawValue)
        {
        }

        protected override Unit GetUnit()
        {
            return Unit.Hz;
        }
    }

    public class KiloHerz : Frequency
    {
        public KiloHerz(int rawValue) : base(rawValue)
        {
        }

        protected override Unit GetUnit()
        {
            return Unit.KHz;
        }
    }

    public class GigaHerz : Frequency
    {
        public GigaHerz(int rawValue) : base(rawValue)
        {
        }

        protected override Unit GetUnit()
        {
            return Unit.GHz;
        }
    }

And on the top of that - extension methods:

    public static Frequency Hz(this int frequency)
    {
        return new Frequencies.Herz(frequency);            
    }

    public static Frequency KHz(this int frequency)
    {
        return new Frequencies.KiloHerz(frequency);
    }

    public static Frequency GHz(this int frequency)
    {
        return new Frequencies.GigaHerz(frequency);
    }

Now you can use it as follows:

5.Hz().ToKHz().ToHz();

etc. and it's safe, you can also strongly type your methods:

public void DoSomething(Herz frequency)

and you're protected against accidentally passing a KHz value to it.

\$\endgroup\$
  • \$\begingroup\$ ++ Interesting idea, the only thing I don't like is the base class knowing about its derived types. \$\endgroup\$ – Mathieu Guindon Nov 2 '15 at 20:45
  • 1
    \$\begingroup\$ @Mat'sMug you're right, it's a design compromise (but what cleaner way is there?). On the other hand, let's say it's pragmatic about bending some OOP principles :) This is not your average class "family", after all - it's more of an emulated Java enum, or a multiton; we don't want anyone from outside to ever add more child classes, quite the contrary, the list is supposed to be closed. The Unit enum can't be extended on the outside, and this class hierarchy mirrors its values. \$\endgroup\$ – Konrad Morawski Nov 2 '15 at 21:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.