C# 将泛型限制为可以为null的对象
我想将我正在编写的泛型代码限制为任何可以为C# 将泛型限制为可以为null的对象,c#,generics,null,nullable,C#,Generics,Null,Nullable,我想将我正在编写的泛型代码限制为任何可以为null的内容。这基本上是任何类+系统。可以为null(例如int?等等) 对于课堂部分,这相当简单: public class MyGeneric<T> where T : class {} 但是不行。编译器返回以下错误: 错误CS0717:“System.Nullable”:静态类不能用作约束 然后我试着 public class MyGeneric<T> where T : class, INullable {} 此程序
null
的内容。这基本上是任何类+系统。可以为null
(例如int?
等等)
对于课堂部分,这相当简单:
public class MyGeneric<T> where T : class {}
但是不行。编译器返回以下错误:错误CS0717:“System.Nullable”:静态类不能用作约束 然后我试着
public class MyGeneric<T> where T : class, INullable {}
此程序在调试控制台中打印
abcg
。否,您不能将泛型限制为只能为null的内容。例如,见。提供的唯一解决方案是使用default(T)
替代,或者使用class
限制,因为无法仅限制可为空的类型
不清楚你的目的是什么。只要更改示例代码的几行,它就可以处理任何类型,而不仅仅是可为null的类型,所以我不明白为什么要限制它
此示例也适用于MyGeneric
或MyGeneric
:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Test
{
public class MyGeneric<T> // removed "where T : class"
{
public void Print(params T[] vals)
{
Print((IEnumerable<T>) vals);
}
public void Print(IEnumerable<T> vals)
{
foreach (var v in vals.OfType<T>()) // use "OfType" instead of "Where"
{
Trace.WriteLine(v.ToString());
}
Trace.WriteLine(string.Empty);
}
}
class Program
{
static void Main(string[] args)
{
MyGeneric<string> foo = new MyGeneric<string>();
foo.Print("a", "b", "c", null, null, "g");
}
}
}
使用System.Collections.Generic;
使用系统诊断;
使用System.Linq;
名称空间测试
{
公共类MyGeneric//删除了“where T:class”
{
公共作废打印(参数T[]VAL)
{
打印((IEnumerable)VAL);
}
公共作废打印(IEnumerable VAL)
{
foreach(vals.OfType()中的var v)//使用“OfType”而不是“Where”
{
Trace.WriteLine(v.ToString());
}
Trace.WriteLine(string.Empty);
}
}
班级计划
{
静态void Main(字符串[]参数)
{
MyGeneric foo=新的MyGeneric();
foo.Print(“a”、“b”、“c”、null、null、g”);
}
}
}
不,在编译时无法执行此操作
就我个人而言,我只要让T
成为任何东西,然后在静态构造函数中检查它的有效性:
public class MyGeneric<T>
{
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)));
}
}
}
公共类MyGeneric
{
静态MyGeneric()
{
var def=默认值(T);
if(def是ValueType&&Nullable.getunderyingtype(typeof(T))==null)
{
抛出新的InvalidOperationException(
Format(“无法使用不可为空的类型进行实例化:{0}”,
类型(T));
}
}
}
有时,类型系统根本无法实现您想要的功能。在这些情况下,你要么改变你想要的,要么围绕它工作
考虑Tuple
类的示例。它的“最大”版本看起来像Tuple
,其中TRest
必须是Tuple
。这不是编译时限制,而是严格的运行时验证。如果您想要在Foo
中强制执行T
的可空性要求,支持典型类和可空结构,您可能必须求助于类似的方法
/// <summary>
/// Class Foo requires T to be type that can be null. E.g., a class or a Nullable<T>
/// </summary>
/// <typeparam name="T"></typeparam>
class Foo<T>
{
public Foo()
{
if (default(T) != null)
throw new InvalidOperationException(string.Format("Type {0} is not valid", typeof(T)));
}
// other members here
}
//
///类Foo要求T是可以为null的类型。例如,一个类或一个NullableT
///
///
福班
{
公共食物(
{
如果(默认值(T)!=null)
抛出新的InvalidOperationException(string.Format(“类型{0}无效”,typeof(T));
}
//这里的其他成员
}
在这样做的过程中,我记录了类将要求T与可空类型兼容,如果不兼容,则抛出构造函数
Foo<string> foo = new Foo<string>(); // OK
Foo<int?> works = new Foo<int?>(); // also OK
Foo<int> broken = new Foo<int>(); // not OK
Foo-Foo=new-Foo();//好啊
Foo works=new Foo();//也可以
Foo breaked=新的Foo();//不好
示例中不清楚的一点是,为什么要使用类级泛型而不是方法级泛型。根据您的示例,您可以执行以下操作:
public class NonGenericClass
{
public void Print<T>(IEnumerable<T?> vals) where T : struct
{
PrintRestricted(vals.Where(v => v.HasValue));
}
public void Print<U>(IEnumerable<T> vals) where T : class
{
PrintRestricted(vals.Where(v => v != default(T)));
}
private void PrintRestricted<U>(IEnumerable<T> vals)
{
foreach (var v in vals)
{
Trace.WriteLine(v.ToString());
}
Trace.WriteLine(string.Empty);
}
}
公共类非通用类
{
公共作废打印(IEnumerable VAL),其中T:struct
{
PrintRestricted(vals.Where(v=>v.HasValue));
}
公共空白打印(IEnumerable VAL),其中T:class
{
PrintRestricted(vals.Where(v=>v!=默认值(T));
}
私有无效打印限制(IEnumerable VAL)
{
foreach(var v以VAL为单位)
{
Trace.WriteLine(v.ToString());
}
Trace.WriteLine(string.Empty);
}
}
如果编写一个包装方法来进行限制,那么您可以获得相同的功能 值得注意的是,
int?foo=null代码>实际上不是空的。让你假装它是糖。“我想把我正在编写的通用代码限制在任何可能的范围内”-你能举个例子说明你为什么要这么做吗?哼。这就引出了一个问题,int的默认值(T)
是什么。我假设它是null
;有办法“或”他们。我认为你做不到。@Joce,既然int?
是结构Nullable
的简写,你会得到一个new Nullable()
,其中HasValue
成员设置为false。适当的特殊语义允许您在代码中有效地将其视为null。它甚至可以在适用的情况下设置为null。是的,但这与我使用的算法不一样,我需要default(t)
设置为null
@Joce:对不起,我不理解你的问题。如果您只是从不将不可空类型用作T
,那么default(T)
将始终为null
。你只是想防止人们意外地不恰当地使用这个类吗?对不起,我误解了你在“OfType”而不是“where”中所做更改的本质。哼我认为这确实可以很好地满足我的需要!!!谢谢但它并没有回答这个问题:甚至有可能将泛型限制为任何可以是null
,那么,如何限制呢?在静态构造函数中抛出异常是一种糟糕的做法,不是吗?可能会产生难以调试的连锁反应。假设您适当地记录了T
必须为空的事实,并且没有办法强制
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Test
{
public class MyGeneric<T> // removed "where T : class"
{
public void Print(params T[] vals)
{
Print((IEnumerable<T>) vals);
}
public void Print(IEnumerable<T> vals)
{
foreach (var v in vals.OfType<T>()) // use "OfType" instead of "Where"
{
Trace.WriteLine(v.ToString());
}
Trace.WriteLine(string.Empty);
}
}
class Program
{
static void Main(string[] args)
{
MyGeneric<string> foo = new MyGeneric<string>();
foo.Print("a", "b", "c", null, null, "g");
}
}
}
public class MyGeneric<T>
{
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)));
}
}
}
/// <summary>
/// Class Foo requires T to be type that can be null. E.g., a class or a Nullable<T>
/// </summary>
/// <typeparam name="T"></typeparam>
class Foo<T>
{
public Foo()
{
if (default(T) != null)
throw new InvalidOperationException(string.Format("Type {0} is not valid", typeof(T)));
}
// other members here
}
Foo<string> foo = new Foo<string>(); // OK
Foo<int?> works = new Foo<int?>(); // also OK
Foo<int> broken = new Foo<int>(); // not OK
public class NonGenericClass
{
public void Print<T>(IEnumerable<T?> vals) where T : struct
{
PrintRestricted(vals.Where(v => v.HasValue));
}
public void Print<U>(IEnumerable<T> vals) where T : class
{
PrintRestricted(vals.Where(v => v != default(T)));
}
private void PrintRestricted<U>(IEnumerable<T> vals)
{
foreach (var v in vals)
{
Trace.WriteLine(v.ToString());
}
Trace.WriteLine(string.Empty);
}
}