5

引数なしのコンストラクターの呼び出しを禁止する必要がある ac# 構造体があります。

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()その結果がポインターになり、これが私にとって厄介なことになっています。ただし、この質問はそれにもかかわらず関連性があるため、無視してください。

4

5 に答える 5

14

できません。すべての構造体には、C# の定義により、パラメーターなしのパブリック コンストラクターがあります。(CLR では、それらのほとんどは実行されませんが、常に実行されているかのように動作します。)構造体で独自のパラメーターなしのコンストラクターを定義できない理由については、この質問を参照してください (とにかく C# で)。

実際、値の型を IL で記述しても問題ない場合は、このステートメントを回避できます。確認したところ、値の型にパラメーターなしのコンストラクターのみが含まれていることを確認し、それを内部にすると、次のように記述できなくなります。MyStruct ms = new MyStruct();しかし、これは妨げません:

MyStruct[] array = new MyStruct[1];
MyStruct ms = array[0];

これは新しい制限を回避するため、実際には何も購入しません。ILをいじるのは面倒なので、それは本当に良いことです。

そもそも本当に構造体を書きたいと思っていますか? それはほとんど決して良い考えではありません。

于 2008-12-04T20:37:43.220 に答える
3

できません。

構造体のすべての値は構築時に初期化する必要があり、コンストラクターの外でそれを行う方法はありません。

それを行うことによって、正確に何を達成しようとしていますか?構造体は値型であるため、ほとんどの操作で「新しい」構造体が得られます。ファクトリを使用するような種類の制約を構造体に適用することは非常に困難です。

于 2008-12-04T20:38:33.483 に答える
2

構造体にアクセスできる限り、誰でもコンストラクターを呼び出さずにいつでも構造体を作成できます。次のように考えてください。

1000 個の要素を持つオブジェクトの配列を作成すると、それらはすべて null に初期化されるため、コンストラクターは呼び出されません。

構造体では、null などはありません。1000 個の DateTime オブジェクトを含む配列を作成すると、それらはすべてゼロに初期化され、DateTime.Min に等しくなります。ランタイムの設計者は、コンストラクターを N 回呼び出さずに構造体の配列を作成できるようにすることを選択しました。これは、多くの人が気付いていないパフォーマンス ヒットです。

しかし、あなたの工場のアイデアは良いものです。インターフェイスを作成して公開する必要がありますが、構造体をプライベートまたは内部にしますか? それはあなたが得るのと同じくらい近いです。

于 2008-12-04T20:40:44.960 に答える
0

デフォルトの初期化状態にあるかどうかを検出する構造体を作成し、その場合に適切な処理を行うことができます。ここでは Factory をそのまま残しましたが、一般的な単純なケースでは、コンストラクターも適切な Factory になる可能性があります。

定型コードがたくさんあります。D を使用しているので、「C にテンプレート mixin があればいいのに」と私と同じことを考えているかもしれません。

例:

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();
        }
    }
}
于 2016-01-29T15:32:55.890 に答える
0

それを独自のアセンブリに入れ、引数なしの MyStruct() を内部 (VB のフレンド) として持ちます。Factory を MyStruct() と同じアセンブリに配置しますが、Public アクセサーを使用します。

これで、ファクトリは引数なしの MyStruct にアクセスできますが、アセンブリの外部から呼び出すものはすべて、ファクトリを使用する必要があります。

編集:悪いことに、これが構造体であることを考慮に入れていませんでした。これを構造体で行うことはできず、クラスでのみ行うことができます。その場合、前のステートメントが有効になります。

于 2008-12-04T20:37:12.177 に答える