我遇到了Marc Gravell 关于如何在不调用其构造函数的情况下创建对象的答案。有人可以确认这甚至不会绕过单例模式的完整和最佳实现(参考实现在这里。为什么?我想更具体地说,我不清楚 GetSafeUninitializedObject() 在类的构造函数上下文中的内部工作原理(静态,私人等)
4 回答
在单例模式中,您的类型上有一个静态变量,该变量将由类型构造函数初始化。
通过调用GetSafeUninitializedObject
,您只能避免在类型构造函数之后调用的实例构造函数。
例子:
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private static string _StaticMessage = "Type ctor;";
private string _Message = "init; ";
static Singleton()
{ }
private Singleton()
{
_Message += "ctor; ";
}
public static Singleton Instance
{
get { return instance; }
}
public string Message { get { return _StaticMessage + _Message; } }
}
internal class Program
{
private static void Main(string[] args)
{
var singleton = Singleton.Instance;
// writes "Type ctor;init; ctor;"
Console.WriteLine(singleton.Message);
var instance = (Singleton)System.Runtime.Serialization.FormatterServices
.GetSafeUninitializedObject(typeof(Singleton));
// writes "Type ctor;"
Console.WriteLine(instance.Message);
}
}
更新以澄清 IllidanS4 要求的类型初始化程序和静态 ctor 之间的区别
这并不真正属于上面的答案,而是属于评论中的问题:答案根本不适合简单的评论。
@IllidanS4:类型初始化器只是编写隐式静态构造函数的快捷方式。如果您创建一个包含两种初始化方法的类并反编译生成的程序集,您只能看到一个静态构造函数 (.cctor),它将初始化所有变量。这两个赋值将被合并,首先调用类型初始化器,最后调用静态构造函数中的语句。
采取这个示例类:
internal static class C
{
public static readonly string ByTypeCtor;
public static readonly string ByTypeInitializer = "From type init; ";
public static string ByBoth = "From type init; ";
static C()
{
ByTypeCtor = "From static ctor; ";
ByBoth += "From static ctor";
}
}
如果你编译它然后反编译它(例如通过使用ILSpy)你会得到以下代码:
internal static class C
{
public static readonly string ByTypeCtor;
public static readonly string ByTypeInitializer;
public static string ByBoth;
static C()
{
C.ByTypeInitializer = "From type init; ";
C.ByBoth = "From type init; ";
C.ByTypeCtor = "From static ctor; ";
C.ByBoth += "From static ctor";
}
}
由于这个事实,我通常不会在声明变量时直接初始化它。相反,我总是让它们未初始化(如ByTypeCtor
变量)并在构造函数中进行所有初始化。这只是避免了将变量初始化到类中不同位置的混乱,从而提高了可维护性。
正如大家所提到的,它可以规避和破坏您的设计模式。但是看看推荐的用法GetSafeUninitializedObject
根据MSDN:
只有当用户打算立即填充所有字段时,才应使用GetSafeUninitializedObject进行反序列化。它不会创建未初始化的字符串,因为创建不可变类型的空实例没有任何意义。
是的,在大多数情况下,默认的 Singleton 模式肯定更好。您的类型的预期行为是必须调用它的 ctor。
创建一个对象而不调用该类型的 ctor 方法是不“正常的”,所以我个人总是避免使用这个解决方案,直到有非常充分的理由这样做。
旁注:单例模式只是一种模式,而不是框架行为。在我看来,在这个问题中,你混合了两个不能混合在一起的概念。
第一种情况是一种创建对象的方式,第二种情况是一种架构代码的方式。
一般规则是:不要做“奇怪的事情”(GetSafeUninitializedObject
很奇怪),直到你真的需要这样做。继续在所有开发人员模式中共同共享,并尽可能保持简单。
GetSafeUninitializedObject 方法甚至适用于具有私有构造函数的对象,因此它可以潜在地用于规避单例模式。