代码之家  ›  专栏  ›  技术社区  ›  Razor

下面的代码是从单例创建对象的可靠方法吗?

c#
  •  6
  • Razor  · 技术社区  · 15 年前

    我在生产中偶然发现了这个代码,我认为它可能会给我们带来问题。

    internal static readonly MyObject Instance = new MyObject();
    

    两次调用实例字段将返回具有相同哈希代码的两个对象。这些物体可能是不同的吗?

    我对cli的了解表明它们是相同的,因为散列码是相同的。

    有人能解释一下吗?

    8 回复  |  直到 15 年前
        1
  •  6
  •   Philippe Leybaert    15 年前

    该字段将只初始化一次,因此您将始终获得相同的对象。非常安全。

    当然,在使用来自多个线程的静态对象时必须小心。如果对象不是线程安全的,您应该在从不同的线程访问它之前锁定它。

        2
  •  6
  •   Marc Gravell    15 年前

    是的,它是安全的——最简单的安全单例实现。

    作为比较哈希代码来推断“它们是同一个对象”的另一点;由于我们在这里讨论的是引用类型(对于值类型来说,singleton是无意义的),检查两个引用是否指向同一个对象的最佳方法是:

    bool isSame = ReferenceEqual(first, second);
    

    这不依赖于 GetHashCode() / Equals / == 实现(它查看引用本身)。

        3
  •  5
  •   Hans Passant    15 年前

    clr提供的一个保证是,即使类由多个线程使用,它也能正常工作。这在ECMA 335第二部分第10.5.3.3节中有规定:

    在多线程系统中进行类型初始化时,也存在类似但更复杂的问题。例如,在这些情况下,两个单独的线程可能开始尝试访问 类型(A和B),然后每个类型都必须等待另一个类型完成初始化。

    确保上述第1点和第2点的算法的大致轮廓如下:
    1。在类加载时(因此在初始化之前),将零或空存储到类型的所有静态字段中。
    2。如果类型已初始化,则完成操作。
    2.1。如果类型尚未初始化,请尝试获取初始化锁。
    2.2。如果成功,将此线程记录为负责初始化类型并继续执行步骤2.3。
    2.2.1如果不成功,请查看此线程或等待此线程完成的任何线程是否已保留 锁。
    2.2.2.如果是这样,则返回,因为阻塞会导致死锁。此线程现在将看到一个未完全初始化的 类型的状态,但不会出现死锁。
    2.2.3如果不是,则阻塞直到类型初始化,然后返回。
    2.3初始化基类类型,然后初始化此类型实现的所有接口。
    2.4执行此类型的类型初始化代码。
    2.5将类型标记为已初始化,释放初始化锁,唤醒等待此类型的任何线程 初始化并返回。

    很明显,这是他们为clr实现建议的算法,而不是您的代码。

        4
  •  3
  •   David M    15 年前

    其他答案也评论了岩石的安全性。以下是关于哈希代码的更多参考:

    哈希代码相同意味着这两个对象 可以 被考虑 “平等” -不同于“相同”的概念。所有的哈希代码都告诉你,如果两个对象 不同的哈希代码 他们是 一定地 “平等” -因此,含蓄地说,绝对不是“相同的”。平等的定义是 .Equals() 方法,如果两个对象被此方法视为相等,则它们必须从其 .GetHashCode() 方法。如果两个变量的引用相等,则它们是“相同的”——即它们指向内存中的同一个对象。

        5
  •  1
  •   Jesper Fyhr Knudsen    15 年前

    它是静态的,意味着它属于类,并且是只读的,所以在初始化之后不能更改我,所以是的,您将得到相同的对象。

        6
  •  0
  •   Padel    15 年前

    在这种情况下,它工作得很好,但是如果您用[threadstatic]属性标记实例,那么内联初始化将无法工作,您将不得不使用其他东西,例如惰性初始化,在这种情况下,您不必担心使用singleton的操作是否是“线程安全的”,因为singleton是每个线程的。

    当做。。。

        7
  •  0
  •   David Waters    15 年前

    您可能会对初始化的惰性感兴趣 可能会有所不同。

    如果您关心何时 实例实际上已初始化。 为了避免以错误的方式曝光,我向您提供了有关他的链接 关于单例模式的文章。

    您的问题涉及到本文中讨论的第四个(并建议)单例模式实现。

    Singleton: singleton implementation

    在本文中,您可以找到一个链接,指向有关beforefieldinit和初始化的惰性的讨论。

        8
  •  -2
  •   ErikHeemskerk    15 年前

    您认为它们是相同的,因为散列码是相同的,这是不正确的, GetHashCode() 对对象的字段进行比较。

    假设你没有超载 Object.Equals ,您可以执行简单的等于比较,默认情况下是参照比较:

    MyObject a = MyObject.Instance;
    MyObject b = MyObject.Instance;
    
    Console.WriteLine(a == b);
    

    这将输出 True 顺便说一下,因为您的单例实现是正确的。一 static readonly 字段只能分配一次。但是,从语义上讲,只使用get访问器实现属性并使用私有静态字段作为后备存储更为正确。