Matthew Layton
Matthew Layton

Reputation: 42260

Why does Color use Int32 over Byte?

Using this example:

Color.FromArgb(Int32, Int32, Int32)

Creates a Color structure from the specified 8-bit color values (red, green, and blue). The alpha value is implicitly 255 (fully opaque). Although this method allows a 32-bit value to be passed for each color component, the value of each component is limited to 8 bits.

If the value of each component is limited to 8 bits, then why didn't they use Byte instead of Int32?

In broader scope, I find people using Int32 very commonly, even when Int16 or Byte would suffice. Is there any particular reason for using Int32 generally over Int16, Byte, etc?

Upvotes: 9

Views: 2360

Answers (3)

EvilTak
EvilTak

Reputation: 7579

The Color struct itself uses the byte type for the R, G, B and A components.

I think that they've chosen to use int as the parameter types even when byte would suffice because it'll save the user from casting back to a type when converting an ARGB representation of the color to the Color struct, as the name says. Which would be done as:

int argb = 0xffff0000; // Red
Color red = Color.FromArgb((argb & 0xff000000) >> 24, (argb & 0x00ff0000) >> 16, (argb & 0x0000ff00) >> 8, argb & 0x000000ff);

Making the coder cast each character to a byte manually would lengthen that line by "(byte)()".Length * 4 = 32 characters.

Plus, they can and do raise an ArgumentException when the values are greater than 255 or less than 0 to properly inform the user that they've entered the wrong input, instead of just clamping the values. Maybe they forgot to normalize the components after using a higher-precision Color representation?

Upvotes: 0

Anders Marzi Tornblad
Anders Marzi Tornblad

Reputation: 19305

I don't think there is a good reason. First, I thought that digging into the code would provide some insight, but all I can find is that there are checks to ensure that the values of alpha, red, green and blue are within the [0..255] range, throwing an exception if not. Internally, the MakeArgb method is then called, which does use byte:

/// <summary>
///     [...]
///     Although this method allows a 32-bit value
///     to be passed for each component, the value of each
///     component is limited to 8 bits.
/// </summary>
public static Color FromArgb(int alpha, int red, int green, int blue)
{
    Color.CheckByte(alpha, "alpha");
    Color.CheckByte(red, "red");
    Color.CheckByte(green, "green");
    Color.CheckByte(blue, "blue");
    return new Color(Color.MakeArgb((byte)alpha, (byte)red, (byte)green, (byte)blue), Color.StateARGBValueValid, null, (KnownColor)0);
}

private static long MakeArgb(byte alpha, byte red, byte green, byte blue)
{
    return (long)((ulong)((int)red << 16 | (int)green << 8 | (int)blue | (int)alpha << 24) & (ulong)-1);
}

private static void CheckByte(int value, string name)
{
    if (value < 0 || value > 255)
    {
        throw new ArgumentException(SR.GetString("InvalidEx2BoundArgument",
                                    name,
                                    value,
                                    0,
                                    255));
    }
}

I guess it just became that way in the early days (we're talking .NET 1.0 here) and then it just stuck.

Also, there is the FromArgb(int) overload, which lets you set all 32 bits with one 32 bit value. Strangely, this is an int and not a unsigned int.

Upvotes: 1

usr
usr

Reputation: 171178

My guess is that byte is less well supported by some .NET languages. Maybe it's not CLS compliant, I don't remember. These days nobody cares about CLS compliance anymore but in the 1.0 days this was an important feature level. Also note that VB.NET does not support unsigned types well which is an example of different support for integers across .NET languages.

Using int for the constructor is especially weird because the A, R, G, B properties are byte.

I consider this to be an API design error.

The Color struct is not particularly beautiful in general. It does not only have ARGB values but also a KnownColor and a name. Many concerns have been crammed into this struct. For fun, the Equals method has a bug: return name.Equals(name);. This is always true, of course. This struct looks hastily done. You can tell from the Equals method that the code author did not know that strings have an overloaded equality operator. The operator equals of Color is the same 15 lines just copied over. I guess the true answer for this question is: The intern did it!


Generally, Int32 is preferred because most arithmetic operations widen to 32 bits and it's an efficient integer width for common hardware. The shorter integer types are for more special uses.

Since it was suggested that this saves downcasting: I do not understand that point. Widening integer conversions are implicit and have no meaningful performance cost (often none).

Upvotes: 6

Related Questions