Anthony
Anthony

Reputation: 9581

Why does this color comparison fail?

Why does this assertion fail?

Assert.AreEqual( Color.Red, Color.FromArgb( Color.Red.A, Color.Red.R, Color.Red.G, Color.Red.B ) );

Upvotes: 10

Views: 5365

Answers (5)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236308

Here is how Colors comparison implemented:

public override bool Equals(object obj)
{
    if (obj is Color)
    {
        Color color = (Color) obj;
        if ((this.value == color.value)
            && (this.state == color.state)
            && (this.knownColor == color.knownColor))
        {
            return ((this.name == color.name)
                || ((this.name != null)
                   && (color.name != null)
                   && this.name.Equals(color.name)));
        }
    }
    return false;
}

How it works:

  1. Field value compared. It is Argb values of current instance (they are stored in one field of type long). So, if Argb different, we have different colors. You pass this step successfully.
  2. Field state compared. It shows the way how color was created: from Argb, from KnownColor, or by name. Actually your comparison fails on this step.
  3. Field knownColor compared. It has value of KnownColor enum, or zero, if color was not created from known color.
  4. Field name compared. It has null value for all colors except ones created by name.

So, if you want to compare values of colors, you should use value field in comparison (it is returned by ToArgb method):

Assert.AreEqual(color1.ToArgb(), color2.ToArgb());

Edit

To create copy of some color you can use following extension method:

public static class ColorHelper
{
    public static Color Copy(this Color color)
    {
        if (color.IsKnownColor)
            return Color.FromKnownColor(color.ToKnownColor());

        if (color.IsNamedColor)
            return Color.FromName(color.Name);

        // this is better, then pass A,r,g,b separately
        return Color.FromArgb(color.ToArgb()); 
    }

Now assertion passes:

Assert.AreEqual(Color.Red, Color.Red.Copy());

But it makes no sense, because you can use single color instance in every place of your program :)

Upvotes: 12

daryal
daryal

Reputation: 14929

Since Color is a struct, it has many properties other than just ARGB values. For instance, if you create a color using two different approaches, they will have different names; thus they will not be equal.

        Color a = Color.Red;            
        Color b = Color.FromArgb(a.A, a.R, a.G, a.B);

        string name1 = a.Name; //name is Red
        string name2 = b.Name; //name is ffff0000

Structs do not have any equality logic by themselves(i.e. if you want to use '==').Thus for every struct, this operator shall be defined. If you investigate Color, you will see the following definition of '==' operator. It depends on how this operator is implemented.

    // Summary:
    //     Tests whether two specified System.Drawing.Color structures are equivalent.
    //
    // Parameters:
    //   left:
    //     The System.Drawing.Color that is to the left of the equality operator.
    //
    //   right:
    //     The System.Drawing.Color that is to the right of the equality operator.
    //
    // Returns:
    //     true if the two System.Drawing.Color structures are equal; otherwise, false.
    public static bool operator ==(Color left, Color right);

Also 'Equals' is overriden in the struct such that it checks for equivalence of structs;

    // Summary:
    //     Tests whether the specified object is a System.Drawing.Color structure and
    //     is equivalent to this System.Drawing.Color structure.
    //
    // Parameters:
    //   obj:
    //     The object to test.
    //
    // Returns:
    //     true if obj is a System.Drawing.Color structure equivalent to this System.Drawing.Color
    //     structure; otherwise, false.
    public override bool Equals(object obj);

Upvotes: 2

Kent Boogaart
Kent Boogaart

Reputation: 178810

This is a consequence of the way the System.Drawing.Color structure is implemented. It has a separate Name property, which is "Red" for Color.Red but is different when you create your own Color from Color.Red. System.Windows.Media.Color (ie. WPF's Color implementation) does not exhibit this same problem.

Upvotes: 2

Gene
Gene

Reputation: 4242

The Equals override of the Color class checks, whether both colors are named (known) colors or not:

public override bool Equals(object obj)
{
    if (obj is Color)
    {
        Color color = (Color) obj;
        if (((this.value == color.value) && (this.state == color.state)) && (this.knownColor == color.knownColor))
        {
            return ((this.name == color.name) || (((this.name != null) && (color.name != null)) && this.name.Equals(this.name)));
        }
    }
    return false;
}

Upvotes: 2

Thomas Levesque
Thomas Levesque

Reputation: 292705

Color.Red is a named color, while Color.FromArgb(...) is not. So they're not considered equal, even though they have the same ARGB values. Note that the string representation is also different:

Color.Red.ToString()           : "Color [Red]"
Color.FromArgb(...).ToString() : "Color [A=255, R=255, G=0, B=0]"

Upvotes: 2

Related Questions