3

这不是一个真正的优化问题或任何东西。这本质上是一个到底是怎么回事的问题。单例模式用于多次使用对象的单个实例。这一切都很好,但是如果我尝试用结构做类似的模式,它不会得到一个实例。

我开始尝试ColorSystem.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

前三个结果是意料之中的,但最后一个让我感到惊讶。现在,我最好的猜测是当_redColors.Red它返回时返回一个副本,就像普通值类型一样。因此,虽然只有一个 的实例_red,但Colors.Red返回一个全新的实例,这就是存储在 redOne 和 redTwo 中的内容。我的想法正确吗?

另外,如果这正确的,static在结构上使用关键字有什么意义吗?

谢谢

4

3 回答 3

7

ReferenceEquals 不适用于结构/值类型,只能用于类。

在两个结构上调用 ReferenceEquals 会将每个结构“装箱”到它自己的内存位置,然后比较两个(显然)不同的地址。

由于类已经是引用类型,因此对 ReferenceEquals 的调用会比较实例的实际地址。

本质上,您不需要存储“Red”的静态副本。将代码更改为以下内容:

class Colors
{
    public static Color Red
    {
        get
        {
            return Color.FromArgb(0xFF, 0xFF, 0x42, 0x39);
        }
    }
}

补充阅读: MSDN - 结构(C# 编程指南)

于 2013-01-03T00:01:54.740 回答
6

因此,虽然 _red 只有一个实例,但 Colors.Red 返回一个全新的实例,这就是存储在 redOne 和 redTwo 中的内容。我的想法正确吗?

是的。您有两个副本,因为 Color 是一种值类型。当您使用 时ReferenceEquals,您将结构体装箱成一个object,并调用Object.ReferenceEquals两个(截然不同的)装箱对象。

另外,如果这是正确的,那么在结构上使用 static 关键字有什么意义吗?

这里的重点不是让它成为一个单例——它是为了简化 API,这样你就可以得到一个颜色的有效常量值:Colors.RedColors.Green. 如果不是静态的,您每次都需要手动创建“红色”颜色。在这种情况下,属性实际上是Colors类上看起来更好看的工厂方法。

于 2013-01-03T00:03:14.827 回答
0

该变量_red将保存一个Color初始化一次的实例。因为 .net 没有提供任何方法来返回对结构实例的引用,但是,每次对属性的调用都Red将返回一个新的结构实例,其公共和私有字段都使用从_red. 进一步,因为值类型字段不存储对象,而只是数据,可以根据需要隐式复制到堆对象中(每个值类型都有对应的堆对象类型;两者都由同一个Type对象描述,但它们不是同一个类型),每次将值类型转换为堆类型(例如Object)时,都会使用从值类型实例初始化的字段值创建一个新的堆对象实例。

于 2013-01-03T05:55:52.730 回答