【发布时间】:2012-04-17 03:38:25
【问题描述】:
为什么这个断言会失败?
Assert.AreEqual( Color.Red, Color.FromArgb( Color.Red.A, Color.Red.R, Color.Red.G, Color.Red.B ) );
【问题讨论】:
-
这样做:Color.Red.ToArgb() == Color.FromArgb(255, 0, 0).ToArgb()
为什么这个断言会失败?
Assert.AreEqual( Color.Red, Color.FromArgb( Color.Red.A, Color.Red.R, Color.Red.G, Color.Red.B ) );
【问题讨论】:
Color.Red 是一种命名颜色,而Color.FromArgb(...) 不是。所以它们不被认为是相等的,即使它们具有相同的 ARGB 值。请注意,字符串表示形式也不同:
Color.Red.ToString() : "Color [Red]"
Color.FromArgb(...).ToString() : "Color [A=255, R=255, G=0, B=0]"
【讨论】:
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;
}
【讨论】:
由于 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。
Name 如果实际上没有字符串,则只生成字符串。我指出了这一点,因为比较不使用属性Name。仅使用实际字段name。
这是System.Drawing.Color 结构实现方式的结果。它有一个单独的Name 属性,对于Color.Red 是“红色”,但是当您从Color.Red 创建自己的Color 时会有所不同。 System.Windows.Media.Color(即 WPF 的 Color 实现)不会出现同样的问题。
【讨论】:
以下是颜色比较的实现方式:
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;
}
它是如何工作的:
value 比较。它是当前实例的 Argb 值(它们存储在 long 类型的一个字段中)。所以,如果 Argb 不同,我们就有不同的颜色。您成功通过了这一步。state。它显示了颜色的创建方式:来自 Argb、来自 KnownColor 或按名称。 实际上你的比较在这一步失败了。
knownColor 比较。如果颜色不是从已知颜色创建的,则它的值为 KnownColor 枚举或零。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());
但这没有任何意义,因为您可以在程序的每个地方使用单一颜色实例:)
【讨论】: