16

私はこのようなサンプルの使用法を探しています:

Foo<string> stringFoo = new Foo<string>("The answer is");
Foo<int> intFoo = new Foo<int>(42);
// The Value of intFoo & stringFoo are strongly typed
stringFoo.Nullify();
intFoo.Nullify();
if (stringFoo == null && intFoo == null)
   MessageBox.Show("Both are null);

このクラスFooが与えられると、Tをnull許容型に自動ラップできます。

public class Foo1<T>
   where T : struct
{
   private T? _value;

   public Foo(T? initValue)
   {
      _value = initValue;
   }

   public T? Value { get { return _value; } }

   public void Nullify { _value = null; }

}

これはプリミティブでは機能しますが、Stringや他のクラスでは機能しません。

次のフレーバーは文字列に対して機能しますが、プリミティブに対しては機能しません。

public class Foo2<T>
{
   private T _value;

   public Foo(T initValue)
   {
      _value = initValue;
   }

   public T Value { get { return _value; } }

   public void Nullify { _value = default(T); }

}

Nullable<int>Foo2に使用でき、コードは次のように機能します。

Foo2<int?> intFoo = new Foo<int?>(42);

ただし、Foo2では失敗するため、これはエラーが発生しやすくなります。Tをnull可能性をサポートするタイプに制約できれば、それで問題ありません。

それで、その後、Tをnull許容型に制約する方法はありますか?

いくつかの追加の注意事項:.NET 4.0、VS2010。そして、私はここでこれに似た質問を1つ見つけましたが、成功した答えはありませんでした。

4

3 に答える 3

20

これに適用できる制約はありませんが、実行時にテストできます。

if (default(T) != null)
{
    throw new SomeAppropriateException(typeof(T) + " is not a nullable type");
}

それを静的コンストラクターに入れることもできます。これにより、構築された型ごとに 1Foo<int>だけ実行されるようになります。また、どこでも使用しようとすると、 TypeInitializerException. これは公開APIにとってはあまり友好的ではありませんが、内部 API にとっては妥当だと思います。

編集:のインスタンスを作成するのを難しくする恐ろしいFoo<int>方法が 1 つあります ...このブログ投稿の恐ろしいコードを使用して(オーバーロードの解決規則を既定のパラメーターといくつかの制約されたジェネリック型と共に使用して)、そのオーバーロードにマークを付けることができます。エラーで廃止されたものとして、null を許容しない値の型を目指しています。そうFoo<int>すれば、まだ有効な型になりますが、そのインスタンスを作成するのは難しいでしょう。ただし、これを行うことをお勧めするつもりはありません...

于 2013-02-15T13:27:19.887 に答える
11

internalのコンストラクターをFoo<T>作成し、新しいインスタンスがファクトリ クラスを介してのみ作成できるようにすることができる場合があります。

public class Foo<T>
{
    private T value;
    internal Foo(T value)
    {
        this.value = value;
    }

    public void Nullify()
    {
        this.value = default(T);
    }

    public T Value { get { return this.value; } }
}

public class Foo
{
    public static Foo<T> Create<T>(T value) where T : class
    {
        return new Foo<T>(value);
    }

    public static Foo<T?> Create<T>(T? value) where T : struct
    {
        return new Foo<T?>(value);
    }
}
于 2013-02-15T13:43:12.543 に答える
-1

Foo1 の構文ほど好きではありませんが、Foo3 は次のとおりです。

public class Foo3<T>
   where T : struct
{

   private T _value;
   private T _nullValue;

   public Foo3(T initValue)
       : this(initValue, default(T))
   {
   }

   public Foo3(T initValue, T nullValue)
   {
      _value = initValue;
      _nullValue = nullValue;
   }

   public T Value { get { return _value; } }

    public bool IsNull
    {
        get 
        {
           return object.Equals(_value, _nullValue);
        }
    }

    public void Nullify() { _value = _nullValue; }

}

そして、私の使用法は次のようになります。

Foo3<string> stringFoo = new Foo<string>("The answer is");
Foo3<int> intFoo = new Foo<int>(42, int.MinValue);
stringFoo.Nullify();
intFoo.Nullify();
if (stringFoo.IsNull && intFoo.IsNull)
   MessageBox.Show("Both are null);

Foo3 (および Foo2) の Value プロパティを取得するのは簡単ではないため、これでもエラーが発生しやすくなります。値が自動的にラップされるため、Foo1 は最適でした。サポートは null になります。

ValueTypeFoo と ObjectFoo が必要なだけで、2 つのバージョンを処理できます。

于 2013-02-15T14:14:15.503 に答える