C#の構造体でファクトリーの使用を強制するにはどうすればよいですか
質問
No argsコンストラクターの呼び出しを禁止する必要があるc#構造体があります。
MyStruct a;
/// init a by members // OK
MyStruct b = MyStruct.Fact(args); // OK, inits by memebers
MyStruct s = new MyStruct(); // can't have that
有効なデフォルト値がなく、すべてのメンバーが有効な値を持っている必要があるため、これは主にすべてのメンバーに明示的な値を強制するために行っています。
C ++では、これは簡単です。プライベートコンストラクターを追加しますが、c#では許可されません。
上記を防ぐ方法はありますか?
すべてのパブリックコンストラクター呼び出しを防止することで同様に機能するように、ファクトリーの使用を強制する必要があります。
完全な開示者:単一の依存関係を回避するために、c#アプリは自動的にDに変換されますが、new Struct()
はポインターを生成します。ただし、この質問はそれにもかかわらず関連性があるため、無視してください。
解決
できません。すべての構造体には、C#の定義により、パブリックなパラメーターなしのコンストラクターがあります。 (CLRでは、ほとんど何もしませんが、常にそうであるかのように振る舞うことができます。)できる理由については、この質問をご覧ください。 (とにかくC#で)構造体に独自のパラメーターなしのコンストラクターを定義しないでください。
実際には、ILで値の型を記述してよければ、この文を 防止できます。確認したばかりですが、値の型にパラメーターなしのコンストラクターのみが含まれていることを確認し、内部に設定すると、MyStruct ms = new MyStruct();
を書くことができなくなりますが、これは防止できません:
MyStruct[] array = new MyStruct[1];
MyStruct ms = array[0];
これは新しい制限を回避するため、実際には何も購入しません。 ILをいじるのは面倒なので、これは本当に良いことです。
そもそも本当に構造体を作成したいのですか?それは決して良い考えではありません 。
他のヒント
できません。
構造体のすべての値は構築時に初期化する必要があり、コンストラクターの外部でそれを行う方法はありません。
それを行うことで、正確に何を達成しようとしていますか?構造体は値型なので、<!> quot; new <!> quot;を取得します。ほとんどの操作の構造体。構造体でファクトリを使用するような制約を強制することは非常に困難です。
誰でも、構造体にアクセスできる限り、コンストラクターを呼び出さずにいつでも構造体を作成できます。このように考えてください:
1000個の要素を持つオブジェクトの配列を作成すると、それらはすべてnullに初期化されるため、コンストラクターは呼び出されません。
構造体では、nullなどはありません。 1000個のDateTimeオブジェクトを含む配列を作成すると、それらはすべてゼロに初期化され、DateTime.Minと等しくなります。ランタイムの設計者は、コンストラクターをN回呼び出さずに構造体の配列を作成できるようにすることを選択しました。多くの人が気付かないようなパフォーマンスヒットがありました。
しかし、あなたの工場のアイデアは良いものです。インターフェイスを作成して公開する必要はありますが、構造体をプライベートまたは内部にしますか?ほぼ同じくらいです。
それを独自のアセンブリに配置し、内部(VBのフレンド)として引数なしのMyStruct()を使用します。ファクトリはMyStruct()と同じアセンブリにありますが、パブリックアクセサーがあります。
ファクトリは引数なしのMyStructにアクセスできるようになりましたが、アセンブリの外部から呼び出すものはすべてファクトリを使用する必要があります。
編集:悪い、これが構造体であることを考慮に入れなかった。クラスでのみ構造体でこれを行うことはできません-この場合、私の前のステートメントが立っています。
デフォルトの初期化状態にあるかどうかを検出する構造体を作成し、その場合に適切な処理を実行できます。私はファクトリーを残しましたが、一般的な単純なケースでは、コンストラクターも適切なファクトリーになる可能性があります。
多くの定型コードです。 Dを使用しているので、あなたは私と同じことを考えているかもしれません、<!> quot; C#にテンプレートミックスインがあればいいのです。
例:
using System;
namespace CrazyStruct
{
public struct MyStruct
{
private readonly int _height;
private readonly bool _init; // Will be 'false' using default(MyStruct).
/// <summary>
/// Height in centimeters.
/// </summary>
public int Height
{
get
{
if (!_init)
{
// Alternatively, could have the preferred default value set here.
// _height = 200; // cm
// _heightInit = true;
throw new InvalidOperationException("Height has not been initialized.");
}
return _height;
}
// No set: immutable-ish.
}
private MyStruct(int height)
{
_height = height;
_init = true;
}
public static MyStruct Factory(int height)
{
return new MyStruct(height);
}
}
static class Program
{
static void Main(string[] args)
{
MyStruct my = MyStruct.Factory(195);
Console.WriteLine("My height is {0} cm.", my.Height);
try
{
var array = new MyStruct[1];
var ms = array[0];
Console.WriteLine("My height is not {0} cm.", ms.Height);
}
catch (Exception ex)
{
Console.WriteLine("Caught the expected exception: {0}.", ex);
}
Console.ReadKey();
}
}
}