Implicit Conversion

Recently I ran into a problem where I had to dive into the C# bag of tricks. It's not a complicated trick and it's been around for quite some time (which is why I had to dive into the bag, rathen than skim the LINQ surface). But I've never used it until now, so here's a quick review of implicit conversions.

I needed to create a percentage value. Basically, I wanted to restrict a decimal between the values of 0 to 100 inclusive [1]. So rather than spread that rule around in code, I created a tiny struct:

using System;

namespace ImplicitConversion
{
    public struct Percentage
    {
        private decimal value;

        public Percentage(decimal value)
        {
            if(value < 0m || value > 100m)
            {
                throw new ArgumentException("The value must be between 0 and 100 inclusive.", "value");
            }

            this.value = value;
        }

        public decimal Value
        {
            get
            {
                return this.value;
            }
        }
    }
}

OK, that works [2], but then I created a function like this:

static void Report(Percentage one, Percentage two)
{
    Console.Out.WriteLine(one);
    Console.Out.WriteLine(two);
}

and I accidentally used it like this:

Program.Report(10m, 20m);

Of course, to fix it, I could've done this:

Program.Report(new Percentage(10m), 
   new Percentage(20m));

but that felt...unnatural. Why couldn't I just convert it? That would be so cool if I could...hey, wait a minute! C# has implicit conversion:

using System;

namespace ImplicitConversion
{
    public struct Percentage
    {
        private decimal value;

        public Percentage(decimal value)
        {
            if(value < 0m || value > 100m)
            {
                throw new ArgumentException("The value must be between 0 and 100 inclusive.", "value");
            }

            this.value = value;
        }

        public static implicit operator decimal(Percentage value)
        {
            return value.value;
        }

        public static implicit operator Percentage(decimal value)
        {
            return new Percentage(value);
        }

        public decimal Value
        {
            get
            {
                return this.value;
            }
        }
    }
}

And all was well again.

Again, I've never used implict (or explicit) conversion since C# came out. But in this case it seems like a natural fit.

[1] Yes, I realize a percentage can be negative and go beyond the upper range I gave. Just ignore that for now.

[2] I would've like to make it generic, but then I would've wanted to constrain the type to unsigned numerics, and that's not doable in the where clauses. Plus, I just needed to get this done with the decimal type, so I declared YAGNI :).

* Posted at 05.12.2008 02:13:20 PM (Last Update: 05.13.2008 04:18:22 PM) | 1 comment | Link | RSS *

Comments

# Should be Explicit, from Mark Brackett at 05.13.2008 04:18:20 PM

Guidelines state that an implicit conversion is not allowed if there's a possibility for it to throw or lose data: http://msdn.microsoft.com/en-us/library/z5z9kes2(VS.80).aspx

Your conversion from decimal to Percentage should be explicit (requiring a cast), but from Percentage to decimal could be implicit.

Add a Comment

(*) = Required field
Name (*):

E-Mail (*):

Web Site:

Title (*):

Comments (*):

Enter the code you see (*)



Quote
"Successful programmers know how to ask questions, and they know how to ask the right question. You can't go forward until that happens. A programmer is a rigorous scientist determined to coax the truth out of the ones and zeros. There's the beauty. My pitch to programmers, which is far more revolutionary than any programming language or operating system can be, is to look for understanding where you find it, work with people you want to work with, and don't waste time with people who won't listen and aren't grounded in the truth." Dave Winer
Twitter History
follow me on Twitter
Blog History