web-dev-qa-db-ja.com

C#-すべてのデフォルトパラメーターを持つ構造体コンストラクターを呼び出す

今日、大量のデータを保持するためにstructを作成するときに、この問題に遭遇しました。次に例を示します。

_public struct ExampleStruct
{
    public int Value { get; private set; }

    public ExampleStruct(int value = 1)
        : this()
    {
        Value = value;
    }
}
_

上品でダンディに見えます。問題は、値を指定せずにこのコンストラクターを使用しようとし、パラメーターにデフォルト値の1を使用したい場合です。

_private static void Main(string[] args)
{
    ExampleStruct example1 = new ExampleStruct();

    Console.WriteLine(example1.Value);
}
_

このコードは_0_を出力し、_1_は出力しません。その理由は、すべての構造体にパブリックパラメーターのないコンストラクターがあるためです。したがって、明示的なコンストラクターをthis()と呼んでいるように、Mainでも、new ExampleStruct()が実際にExampleStruct()を呼び出している場合と同じことが起こります。 ExampleStruct(int value = 1)を呼び出さない。そのため、intのデフォルト値である0をValueとして使用します。

さらに悪いことに、私の実際のコードは、_int value = 1_パラメーターがコンストラクター内の有効な範囲内にあることを確認しています。これを上記のExampleStruct(int value = 1)コンストラクターに追加します。

_if(value < 1 || value > 3)
{
    throw new ArgumentException("Value is out of range");
}
_

したがって、現在のところ、デフォルトのコンストラクターは、必要なコンテキストでは無効なオブジェクトを実際に作成しました。誰もが私がどのようにできるか知っています:

  • A. ExampleStruct(int value = 1)コンストラクターを呼び出します。
  • B. ExampleStruct()コンストラクターのデフォルト値の設定方法を変更します。
  • C.その他の提案/オプション。

また、Valueプロパティの代わりに次のようなフィールドを使用できることも認識しています。

_public readonly int Value;
_

しかし、私の哲学は、フィールドがconstまたはstaticでない限り、フィールドを非公開で使用することです。

最後に、structの代わりにclassを使用している理由は、これが単に可変でないデータを保持するオブジェクトであり、構築時と渡されたときに完全に入力する必要があるためです。パラメータとして、構造体が設計されている目的であるnull(値によってstructとして渡されるため)にすることはできません。

17
Michael Yanni

実際、MSDNにはstructに関するいくつかの優れたガイダンスがあります。

タイプのインスタンスが小さく、一般的に短命であるか、一般的に他のオブジェクトに埋め込まれている場合は、クラスの代わりに構造を定義することを検討してください。

タイプに次のすべての特性がない限り、構造を定義しないでください。

プリミティブ型(整数、倍精度など)と同様に、論理的に単一の値を表します。

インスタンスサイズが16バイト未満です。

それは不変です。

頻繁に箱詰めする必要はありません。

これらは考慮structの考慮事項であり、「これは常に構造体である必要がある」ではないことに注意してください。これは、structを使用するという選択は、パフォーマンスと使用法に影響を与える可能性があり(正と負の両方)、慎重に選択する必要があるためです。

特に、16バイトを超えるものにはstructを推奨していないことに注意してください(その場合、コピーのコストは参照のコピーよりも高くなります)。

さて、あなたの場合、これを行う良い方法は、デフォルト状態でstructを生成するファクトリを作成するか、プロパティで何らかのtrickを実行して、最初の使用時に初期化するようにだます以外にありません。

structは、new X() == default(X)、つまり、新しく構築されたstructにそのstructのすべてのフィールドのデフォルト値が含まれるように機能することになっていることを忘れないでください。 C#ではnotstructのパラメーターなしのコンストラクターを定義できるため、これは非常に明白ですが、警告なしにすべての引数をデフォルトにできるのは不思議です。

したがって、実際には、classを使用して不変にし、渡されるメソッドでnullを確認することをお勧めします。

public class ExampleClass
{
    // have property expose the "default" if not yet set
    public int Value { get; private set; }

    // remove default, doesn't work
    public ExampleStruct(int value)
    {
        Value = value;
    }
}

ただし、絶対に必要な場合他の理由でstructが必要です-ただし、コピーキャストなどのstructのコストを考慮してください-できますこの:

public struct ExampleStruct
{
    private int? _value;

    // have property expose the "default" if not yet set
    public int Value
    {
        get { return _value ?? 1; }
    }

    // remove default, doesn't work
    public ExampleStruct(int value)
        : this()
    {
        _value = value;
    }
}

デフォルトでは、Nullable<int>null(つまり、HasValue == false)になることに注意してください。したがって、これがtrueの場合、まだ設定しておらず、null合体演算子を使用して代わりに、デフォルトの1。コンストラクターでdidを設定すると、null以外になり、代わりにその値を取得します...

29

structExampleStructを次のようにするのは良い設計ではないと思います

_default(ExampleStruct)
_

つまり、すべてのインスタンスフィールドがゼロ/偽/ヌルである値はnot構造体の有効な値です。そしてあなたが知っているように、あなたが言うとき

_new ExampleStruct()
_

これはdefault(ExampleStruct)とまったく同じであり、すべてのフィールド(自動プロパティから「生成された」フィールドを含む)がゼロである構造体の値を提供します。

多分あなたはこれを行うことができます:

_public struct ExampleStruct
{
  readonly int valueMinusOne;

  public int Value { get { return valueMinusOne + 1; } }

  public ExampleStruct(int value)
  {
    valueMinusOne = value - 1;
  }
}
_
2

コンパイラは実際には、ここで構造体の自動デフォルトctorを選択していると思います http://msdn.Microsoft.com/en-us/library/aa288208(v = vs.71).aspx ではなくデフォルト値でctorを使用します。

追加された参照: http://csharpindepth.com/Articles/General/Overloading.aspx (オプションのパラメーターセクション)

1
fsimonazzi

一部の言語(他に何もない場合はCIL)では、new T()のフィールドが「すべてゼロ」のデフォルト、C#およびvb.net(およびおそらく他のほとんど)以外に設定されるように構造体コンストラクターを定義できます。言語)配列要素やクラスフィールドなどは常にdefault(T)に初期化する必要があるため、new T()と一致しないものに初期化することは混乱を招くと考えてください。 、構造体はnew T()default(T)以外の意味で定義するべきではありません。

パラメータ化されたコンストラクタでデフォルトパラメータをジンクスするのではなく、目的のデフォルト値を返す静的構造体プロパティを定義し、出現するすべてのnew ExampleStruct()を_ExampleStruct.NiceDefault;_に置き換えることをお勧めします。

2015補遺

C#とVB.NETは、パラメーターのない構造体コンストラクターの定義の禁止を緩和する可能性があるようです。これにより、Dim s = New StructType()のようなステートメントがsにタイプStructTypeの新しい配列アイテムに与えられた値とは異なる値を割り当てる可能性があります。 _new StructType_は、C#のdefault(StructType)に類似したものが存在する場合に適している場所でよく使用されるため、私はこの変更にそれほど熱心ではありません。 VB.NETは_Dim s As StructType = Nothing_を許可しますが、それはかなりおかしなようです。

1
supercat

なぜpublic ExampleStruct(int value = 1) : this()があるのですか?

public ExampleStruct(int value = 1)であるべきではありませんか? :this()はパラメーターなしのコンストラクターを作成していると思います。

0
gashach