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&lt;T&gt;
/// </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&lt;T&gt;
/// </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);
    }
}