コンストラクターを呼び出さずにオブジェクトを作成する方法について、Marc Gravell によるこの回答に出会いました。これがシングルトンパターンの完全かつ最良の実装でさえも回避しないことを誰かが確認できますか(ここで実装を参照してください。そして、なぜですか?より具体的には、クラスのコンストラクター(静的、プライベートなど)
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) が 1 つだけ表示されます。両方の割り当ては、型初期化子が最初に呼び出され、静的コンストラクター内のステートメントが最後に呼び出される場所でマージされます。
このサンプルクラスを見てください:
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メソッドを呼び出さずにオブジェクトを作成することは「通常」ではないため、個人的には、本当に強い理由があるまで、このソリューションの使用を常に避けます。
補足: シングルトン パターンは単なるパターンであり、フレームワークの動作ではありません。私の意見では、この質問では、混ざり合わない 2 つの概念を混ぜ合わせています。
最初のケースはオブジェクトを作成する方法で、2 番目のケースはコードを設計する方法です。
GetSafeUninitializedObject
一般的なルールは次のとおりです。本当に必要になるまで、「奇妙なこと」(は奇妙なこと)をしないでください。すべての開発者間で共有される共通のパターンを継続し、物事を可能な限りシンプルに保ちます。
GetSafeUninitializedObject メソッドは、プライベート コンストラクターを持つオブジェクトでも機能するため、シングルトン パターンを回避するために使用される可能性があります。