将generics限制为可以为null的事物
我想限制一个通用的我编码到任何可以为null
东西。 那基本上是任何类+ System.Nullable
(例如int?
等)。
对于课堂部分,它很容易:
public class MyGeneric where T : class {}
但是,这不允许我这样做:
var myGeneric = new MyGeneric();
或这个:
var myGeneric = new MyGeneric<Nullable>();
编译器抱怨:
错误CS0452:类型’int?’ 必须是引用类型才能在generics类型或方法’Test.MyGeneric’中将其用作参数’T’
所以我尝试将addind System.Nullable
作为T
可接受类型:
public class MyGeneric where T : class, System.Nullable {}
但它不会这样做。 编译器返回以下错误:
错误CS0717:’System.Nullable’:静态类不能用作约束
然后我试过了
public class MyGeneric where T : class, INullable {}
它确实编译,但是当我这样做时:
var myGeneric = new MyGeneric();
编译器返回此错误:
错误CS0311:类型’string’不能用作generics类型或方法’Test.MyGeneric’中的类型参数’T’。 没有从’string’到’System.Data.SqlTypes.INullable’的隐式引用转换。
所以,问题是:是否可以将generics限制为任何可以为null
东西,以及如何?
作为参考,我使用的是VS2010 / C#4.0
编辑
我被问到我想用它做什么。 这是一个例子:
namespace Test { public class MyGeneric where T : class { private IEnumerable Vals { get; set; } public MyGeneric(params T[] vals) { Vals = (IEnumerable)vals; } public void Print() { foreach (var v in Vals.Where(v => v != default(T))) { Trace.Write(v.ToString()); } Trace.WriteLine(string.Empty); } } class Program { static void Main(string[] args) { MyGeneric foo = new MyGeneric("a", "b", "c", null, null, "g"); foo.Print(); } } }
该程序在调试控制台中打印abcg
。
不,在编译时没有办法做到这一点。
就个人而言,我只是让T
成为任何东西,然后我会在静态构造函数中检查它的有效性:
public class MyGeneric { static MyGeneric() { var def = default(T); if (def is ValueType && Nullable.GetUnderlyingType(typeof(T)) == null) { throw new InvalidOperationException( string.Format("Cannot instantiate with non-nullable type: {0}", typeof(T))); } } }
不,您不能将generics限制为仅可以为null的内容。 请参阅如何从C#中的generics方法返回NULL? , 例如。 提出的唯一解决方案是使用default(T)
或使用class
限制,因为没有办法限制只有可空类型。
目前尚不清楚你的目的是什么。 更改几行示例代码使其适用于任何类型,而不仅仅是可以为空的类型,因此我不明白为什么要尝试限制它。
此示例也适用于MyGeneric
或MyGeneric
:
using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace Test { public class MyGeneric // removed "where T : class" { public void Print(params T[] vals) { Print((IEnumerable ) vals); } public void Print(IEnumerable vals) { foreach (var v in vals.OfType ()) // use "OfType" instead of "Where" { Trace.WriteLine(v.ToString()); } Trace.WriteLine(string.Empty); } } class Program { static void Main(string[] args) { MyGeneric foo = new MyGeneric (); foo.Print("a", "b", "c", null, null, "g"); } } }
从您的示例中不清楚的一件事是,您希望使用类级别generics而不是方法级别generics来执行此操作。 根据您的示例,您可以执行以下操作:
public class NonGenericClass { public void Print(IEnumerable vals) where T : struct { PrintRestricted(vals.Where(v => v.HasValue)); } public void Print(IEnumerable vals) where T : class { PrintRestricted(vals.Where(v => v != default(T))); } private void PrintRestricted(IEnumerable vals) { foreach (var v in vals) { Trace.WriteLine(v.ToString()); } Trace.WriteLine(string.Empty); } }
对于编写执行限制的包装方法的成本,您可以获得相同的function。
有时类型系统根本无法做到你想要的。 在这些情况下,您可以更改所需内容,也可以解决它。
考虑一下Tuple<>
类的例子。 它的“最大”版本看起来像Tuple
,其中TRest
必须是Tuple<>
。 这不是编译时限制,它严格来说是运行时validation。 如果你想在Foo
强制执行T
的可空性要求,你可能不得不诉诸类似的东西,支持典型的类和可空的结构。
/// /// Class Foo requires T to be type that can be null. Eg, a class or a Nullable<T> /// /// class Foo { public Foo() { if (default(T) != null) throw new InvalidOperationException(string.Format("Type {0} is not valid", typeof(T))); } // other members here }
在这样做时,我正在记录该类将要求T与可空类型兼容,如果不是,则抛出构造函数。
Foo foo = new Foo (); // OK Foo works = new Foo(); // also OK Foo broken = new Foo (); // not OK