views:

116

answers:

7

I am trying to write a function in C# that will take two small int values (range 0-3) and return a Color object based on those values. The problem is that there is no way to programmatically determine the color from the two values, they are specific to a type of LED and must be hardcoded.

The simplest way I can think of would be a massive (16 cases) if-else statement that will check each value, but this doesn't seem like a very elegant solution. Is there a better way to determine the Color?

+1  A: 

As a slightly cleaner alternative to using if / else, you could place the color values in an array, and use the two small int values as indexes into the array.

Justin Ethier
+2  A: 

If you want to go the OO approach (for better or worse), you can use a Tuple->Color mapping:

Dictionary<Tuple<int, int>, Color> d = new Dictionary<Tuple<int, int>, Color>()
{
  {Tuple.Create(2, 1), Color.Red},
  {Tuple.Create(1, 1), Color.Blue},
  {Tuple.Create(1, 2), Color.Green}
};

One advantage over a 2-d array is it can be sparse without nulls. You can also use a collection initializer without initializing in index order. This will throw an obvious ArgumentException at initialization time if you try to use the same tuple for two colors.

Matthew Flaschen
Not sure why this one is getting downvoted. This is the only solution that properly handles sparseness, and is the most extensible of all. You can very easily extend this to the 3-variable case, and you can easily increase or decrease the variable ranges (as well as have different ranges). Not worthy of a downvote at all.
drharris
+2  A: 

What about a two dimensional array of Color objects?

Color[,] colors = new[,] {
    { Color.FromArgb(1, 2, 3), Color.FromArgb(3, 4, 5), Color.FromArgb(6, 7, 8), Color.FromArgb(9, 10, 11) },
    { Color.FromArgb(21, 22, 23), Color.FromArgb(23, 24, 25), Color.FromArgb(26, 27, 28), Color.FromArgb(29, 30, 31) },
};

Color color = colors[index1, index2];
Kirk Woll
If you go the array route, a multi-dimensional or jagged array is certainly better than implementing the multiplier manually. The only downsides are minor counting to check that the mapping is right, and nulls if a sparse mapping is needed.
Matthew Flaschen
This is really all I need for my purposes, but I do like Matthew's suggestion as well. Unfortunately I am working in .NET 3.5 which doesn't support the Tuple class. Thank you all.
Nathan
A: 

The prefered method of avoid a bunch of if else statements is to use Switch. You could also use an enum and assign int values which correspond to your small ints:

public enum ColorChoice
{
  red = 01 //'01' instead of '1' to make clear your first digit is zero
  blue = 02
  green = 03
  ...etc...
]
AllenG
How does this solve his problem of handling two input arguments to resolve the color?
Kirk Woll
@kirk Woll - Last time I checked, you can call an Enum value by it's index. So, input of "0, 1" => "1" => ColorChoice.Red. Which is actually easier to understand (and therefore maintain) than many of the solutions requiring actual math conversion.
AllenG
You can cast an integer to an enum. But you're going to have to do math conversion at some point to. E.g. how do you do 3,1 -> orange. Based on your scheme, it would apparently be orange = 31, which requires math (10 * x + y). Also, you should note that 01 is actually an octal literal; it just happens not to affect the result *here*. Also, C# enums can't have any fields or methods, so you might need another ColorChoice->Color mapping.
Matthew Flaschen
A: 

I vote an enum for the 16 colors

enum Color
{
    red = 0,
    blue = 1
}

public Color GetColor(int v1, int v2)
{
    int colorValue = v1 << 8;
    colorValue |= v2;
    return (Color)colorValue;
}
Jimmy Hoffa
Is using a multiplier really the most readable syntax? I suspect this would send many a programmer to the hills.
Kirk Woll
I could bitshift instead, but I would think that would send a seperate subset of programmers off..
Jimmy Hoffa
-1 What happens when each range goes to 0-4 instead? The whole initialization of the enum changes, as does the formula and everything. Clever, but not practical at all.
drharris
@drharris: now it's safe past 0-4, the formula and the enum values would not change if there were new options added to the LED's..
Jimmy Hoffa
A: 

How about:

static Color GetColor(int c1, int c2)
{
  if (c1 > 3 || c1 < 0 || c2 > 3 || c2 < 0)
    throw new ArgumentOutOfRangeException();

  var map = new Dictionary<string, Color>
  {
    {"00", Color.Black},
    {"10", Color.Red},
    {"20", Color.Blue},
    // etc, etc...
    {"11", Color.DimGray},
  };

  var key = c1.ToString() + c2.ToString();
  return map[key];
}
Bazurbat
A: 

The fact that you have these two small integer values that combine together to mean something (at least a color) suggests to me that it might make sense for you to have a struct defined to encapsulate them.

The benefit of doing that is that you can use the struct as the key to a dictionary: the Equals and GetHashCode methods should already work adequately.

Below is an example. Since you mentioned that the colors cannot be calculated, I've left a comment where the dictionary needs to be populated; you could hard-code this, but ideally you'd read the values from a file or embedded resource.

EDIT: updated my example struct to be immutable.

struct MyStruct
{
    private byte _X;
    private byte _Y;

    public MyStruct(byte x, byte y)
    {
        _X = x;
        _Y = y;
    }

    public byte X { get { return _X; } }
    public byte Y { get { return _Y; } }

    private static Dictionary<MyStruct, Color> _ColorMap;

    static MyStruct()
    {
        _ColorMap = new Dictionary<MyStruct, Color>();

        // read color mapping from somewhere...perhaps a file specific to the type of LED
    }

    public Color GetColor()
    {
        return _ColorMap[this];
    }
}
Dr. Wily's Apprentice