这不是一个优化问题或其他任何问题。这本质上是一个什么样的问题。单例模式用于具有多次使用的对象的单个实例。这一切都很好,但是如果我尝试用结构做类似的模式,它就不会得到单个实例。
我试图用Color
中的System.Drawing
结构做一些事情。以下是一些示例代码:
class Colors
{
private static Color _red;
public static Color Red
{
get
{
if (_red.IsEmpty)
_red = Color.FromArgb(0xFF, 0xFF, 0x42, 0x39);
return _red;
}
}
}
static void Main(string[] args)
{
var redOne = Colors.Red;
var redTwo = Colors.Red;
Console.WriteLine("redOne.Equals(redTwo) : {0}", redOne.Equals(redTwo));
Console.WriteLine("redOne == redTwo : {0}", redOne == redTwo);
Console.WriteLine("Object.Equals(redOne, redTwo) : {0}", Object.Equals(redOne, redTwo));
Console.WriteLine("Object.ReferenceEquals(redOne, redTwo) : {0}", Object.ReferenceEquals(redOne, redTwo));
Console.ReadLine();
}
这个输出是:
redOne.Equals(redTwo) : True
redone == redTwo : True
Object.Equals(redOne, redTwo) : True
Object.ReferenceEquals(redOne, redTwo) : False
前三个结果是可以预期的,但最后一个让我感到惊讶。现在,我最好的猜测是,当从_red
返回Colors.Red
时,它会返回副本,就像正常的值类型一样。因此,虽然_red
只有一个实例,但Colors.Red
会返回一个全新的实例,而这就是存储在redOne和redTwo中的内容。我的想法是否正确?
另外,如果 正确,那么在结构上使用static
关键字是否有任何意义?
由于
答案 0 :(得分:6)
ReferenceEquals不能用于struct / value类型,只能用于类。
在两个结构上调用ReferenceEquals会将每个结构“包装”到它自己的内存位置,然后比较两个(显然)不同的地址。
由于类已经是引用类型,因此对ReferenceEquals的调用会比较实例的实际地址。
基本上,您不需要存储“红色”的静态副本。将代码更改为以下内容:
class Colors
{
public static Color Red
{
get
{
return Color.FromArgb(0xFF, 0xFF, 0x42, 0x39);
}
}
}
答案 1 :(得分:6)
因此,虽然只有一个_red实例,但Colors.Red返回一个全新的实例,这就是存储到redOne和redTwo中的实例。我的想法是否正确?
是。您有两个副本,因为Color是值类型。当您使用ReferenceEquals
时,您将结构框插入object
,并在两个(截然不同的)框对象上调用Object.ReferenceEquals
。
另外,如果这是正确的,在结构上使用static关键字是否有任何意义?
这里的要点不是让它成为单身 - 它是为了简化API,因此你有一个实际上是颜色的常量值:Colors.Red
或Colors.Green
。如果没有静态,则需要每次手动创建“红色”颜色。在这种情况下,属性实际上是Colors
类上看起来更漂亮的工厂方法。
答案 2 :(得分:0)
变量_red
将包含一个Color
实例,该实例初始化一次。因为.net没有提供任何方式来返回对结构实例的引用,所以每次对属性Red
的调用都将返回一个新的结构实例,其字段public和private使用从{{1复制的值进行初始化}}。此外,因为值类型字段不存储对象,而只是存储可以根据需要隐式复制到堆对象的数据(每个值类型都有相应的堆对象类型;两者都由相同的_red
对象描述,但它们每次将值类型转换为堆类型(例如Type
)时,都会创建一个新的堆对象实例,并使用从值类型实例初始化的字段值。