Sometimes you get so caught up in the complexity of an overall problem or solution, that you miss the simplest error even when it is staring you right in the face.
So, the other day, I was working on some c# code for a personal project I'll post here in a few weeks. In it, I needed to create a color based on HSL rather than RGB.
HSL is very useful when you want to generate a number of colors that all have similar characteristics: in this case, I wanted to generate a bunch of random colors all with the same saturation and brightness, but varying hues. Trying to do that in the RGB color space is next to impossible without doing some sort of HSL conversion.
I've built extension methods in the past, and have found them quite helpful in some situations. This sounded like a good use for extension methods, based on an algorithm I found, I built an extension to Color that took in the appropriate HSL values and set the R, G and B properties.
public static Color SetHsl(this Color color, double h, double s, double l)
{
double r, g, b;
// ...
color.R = (byte)(255 * r);
color.G = (byte)(255 * g);
color.B = (byte)(255 * b);
color.A = 255;
}
Now, some of you are likely looking at this code and nodding knowingly. Pat yourself on the back for catching that right away, as I killed a solid hour trying to figure out what on earth I did wrong :)
No matter what I did, the color was always black. It wasn't immediately obvious to me that it was black, because it was also transparent, and on the far end of a gradient.
First, I am not familiar with the math behind the HSL->RGB algorithm, so my first thought was that there was a bug in there. I spent a good chunk of time validating it and walking through it. Secondly, I thought maybe it was a platform bug (yes, we all blame the frameworks before our own code <g>).
The nature of the problem became clear to me when I copied the code over to a new project targeting a different platform (you have to love the .NET framework!) and got the same results.
Enough with the suspense. If you didn't catch the error right away, you're in good company. In the end, it is the absolute simplest first-day-of CS101 error you could make: Color is a struct. Structs are value types, and are passed as copies. So the code below always ended up with an initialized but not set color object - transparent black / #00000000 because the extension method was operating on a copy.
Color color = new Color();
color.SetHsl(rnd.Next(0, 10000) / 10000.0D, 0.75, 0.5);
So in C#, that means you can't have mutator extension methods on value types. That's a useful tidbit to keep in mind.
It turns out this is one place where VB actually has a leg up. In VB.NET, you can make the [me] parameter ByRef. C# doesn't allow ref this parameters in extension methods. (I've seen examples online as to why C# wouldn't allow this, mostly dealing with generic extension methods that could target value and reference types and therefore have unintended effects)
Sometimes it's the simplest stuff that snags us up on projects. :)