将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限制,因为没有办法限制只有可空类型。

目前尚不清楚你的目的是什么。 更改几行示例代码使其适用于任何类型,而不仅仅是可以为空的类型,因此我不明白为什么要尝试限制它。

此示例也适用于MyGenericMyGeneric

 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