我正在寻找类似这样的示例用法:
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自动换行为可空:
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或其他类。
Next flavor适用于字符串,但不适用于原语:
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约束为支持可空性的类型那么那就没问题了。
所以,毕竟有什么方法可以将T限制为可以为空的类型吗?
一些附加说明:.NET 4.0,VS2010。我确实在这里找到了一个类似的问题,但没有成功的答案。
答案 0 :(得分:18)
您可以对此进行约束,但可以在执行时对其进行测试:
if (default(T) != null)
{
throw new SomeAppropriateException(typeof(T) + " is not a nullable type");
}
你甚至可以将它放入一个静态构造函数中,这样可以确保它只对每个构造的类型执行一次 - 任何试图在任何地方使用Foo<int>
的人都会很难忽略TypeInitializerException
。对于 public API来说,这并不是非常友好,但我认为这对内部API来说是合理的。
编辑:有一种可怕的方式使得创建Foo<int>
的实例变得更难......你可以使用ghastly code in this blog post(使用重载决策规则和默认值参数和一些受约束的泛型类型)并将针对非可空值类型的重载标记为过时的错误。这样,Foo<int>
仍然是一个有效的类型,但你很难创建它的实例。我不打算建议你这样做......
答案 1 :(得分:9)
您可以将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);
}
}
答案 2 :(得分:0)
我不喜欢它和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是最好的,因为自动包装Value将为null支持。
我可能只需要ValueTypeFoo和ObjectFoo并处理两个版本。