【问题标题】:Why does this color comparison fail?为什么这个颜色比较会失败?
【发布时间】:2012-04-17 03:38:25
【问题描述】:

为什么这个断言会失败?

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

【问题讨论】:

标签: c# colors


【解决方案1】:

Color.Red 是一种命名颜色,而Color.FromArgb(...) 不是。所以它们不被认为是相等的,即使它们具有相同的 ARGB 值。请注意,字符串表示形式也不同:

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

【讨论】:

    【解决方案2】:

    Color 类的 Equals 覆盖检查两种颜色是否被命名(已知)颜色:

    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;
    }
    

    【讨论】:

      【解决方案3】:

      由于 Color 是一个结构体,它具有许多属性,而不仅仅是 ARGB 值。例如,如果您使用两种不同的方法创建颜色,它们将具有不同的名称;因此它们不会相等。

              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
      

      结构本身没有任何相等逻辑(即,如果您想使用'==')。因此,对于每个结构,都应定义此运算符。如果您研究 Color,您将看到以下 '==' 运算符的定义。这取决于这个运算符是如何实现的。

          // 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);
      

      同样,结构中的“Equals”被覆盖,以便检查结构的等价性;

          // 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);
      

      【讨论】:

      • 不正确。如果是Color b = Color.FromArgb(a.A, a.R, a.G, a.B);,名称将为null
      • @lazyberezovsky 你试过这个吗?我已经尝试过了,它的行为方式与答案中的表述方式相同。
      • Property Name 如果实际上没有字符串,则只生成字符串。我指出了这一点,因为比较不使用属性Name。仅使用实际字段name
      • @lazyberezovsky 我不知道如何实现 equals 或 '==' 运算符,但我想大致了解如何处理两个结构的等效性。如何查看实现此功能的源代码?
      • 有很多很棒的工具,比如 Reflector,或者 Telerik 的 JustDecompile :)
      【解决方案4】:

      这是System.Drawing.Color 结构实现方式的结果。它有一个单独的Name 属性,对于Color.Red 是“红色”,但是当您从Color.Red 创建自己的Color 时会有所不同。 System.Windows.Media.Color(即 WPF 的 Color 实现)不会出现同样的问题。

      【讨论】:

        【解决方案5】:

        以下是颜色比较的实现方式:

        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;
        }
        

        它是如何工作的:

        1. 字段value 比较。它是当前实例的 Argb 值(它们存储在 long 类型的一个字段中)。所以,如果 Argb 不同,我们就有不同的颜色。您成功通过了这一步。
        2. 比较字段state。它显示了颜色的创建方式:来自 Argb、来自 KnownColor 或按名称。 实际上你的比较在这一步失败了。
        3. 字段knownColor 比较。如果颜色不是从已知颜色创建的,则它的值为 KnownColor 枚举或零。
        4. 比较字段name。除了按名称创建的颜色之外,它对所有颜色都有null 值。

        所以,如果你想比较颜色的值,你应该使用value字段进行比较(由ToArgb方法返回):

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

        编辑

        要创建一些颜色的副本,您可以使用以下扩展方法:

        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()); 
            }
        

        现在断言通过了:

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

        但这没有任何意义,因为您可以在程序的每个地方使用单一颜色实例:)

        【讨论】:

          猜你喜欢
          • 2019-10-23
          • 1970-01-01
          • 1970-01-01
          • 2011-07-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-26
          相关资源
          最近更新 更多