C#所有可为null的对象的泛型类型约束

C#所有可为null的对象的泛型类型约束,c#,generics,nullable,C#,Generics,Nullable,所以我有一节课: public class Foo<T> where T : ??? { private T item; public bool IsNull() { return item == null; } } 应该是可能的 使用class作为类型约束只允许我使用引用类型 其他信息: 我正在编写一个pipes and filters应用程序,希望使用null引用作为传递到管道的最后一项,这样每个过滤器都可以很好地关闭、执行清

所以我有一节课:

public class Foo<T> where T : ???
{
    private T item;

    public bool IsNull()
    {
        return item == null;
    }

}
应该是可能的

使用
class
作为类型约束只允许我使用引用类型

其他信息:
我正在编写一个pipes and filters应用程序,希望使用
null
引用作为传递到管道的最后一项,这样每个过滤器都可以很好地关闭、执行清理等操作。

我不知道如何实现等价于泛型或在泛型中实现。但是,我可以建议使用default关键字,以便为可空类型创建null,为结构创建0值:

public class Foo<T>
{
    private T item;

    public bool IsNullOrDefault()
    {
        return Equals(item, default(T));
    }
}
公共类Foo
{
私人物品;
公共bool IsNullOrDefault()
{
返回等于(项目,默认值(T));
}
}
您还可以实现您的Nullable版本:

class MyNullable<T> where T : struct
{
    public T Value { get; set; }

    public static implicit operator T(MyNullable<T> value)
    {
        return value != null ? value.Value : default(T);
    }

    public static implicit operator MyNullable<T>(T value)
    {
        return new MyNullable<T> { Value = value };
    }
}

class Foo<T> where T : class
{
    public T Item { get; set; }

    public bool IsNull()
    {
        return Item == null;
    }
}
类MyNullable,其中T:struct
{
公共T值{get;set;}
公共静态隐式运算符T(MyNullable值)
{
返回值!=null?值,值:默认值(T);
}
公共静态隐式运算符MyNullable(T值)
{
返回新的MyNullable{Value=Value};
}
}
类Foo,其中T:class
{
公共T项{get;set;}
公共bool IsNull()
{
返回项==null;
}
}
例如:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(new Foo<MyNullable<int>>().IsNull()); // true
        Console.WriteLine(new Foo<MyNullable<int>> {Item = 3}.IsNull()); // false
        Console.WriteLine(new Foo<object>().IsNull()); // true
        Console.WriteLine(new Foo<object> {Item = new object()}.IsNull()); // false

        var foo5 = new Foo<MyNullable<int>>();
        int integer = foo5.Item;
        Console.WriteLine(integer); // 0

        var foo6 = new Foo<MyNullable<double>>();
        double real = foo6.Item;
        Console.WriteLine(real); // 0

        var foo7 = new Foo<MyNullable<double>>();
        foo7.Item = null;
        Console.WriteLine(foo7.Item); // 0
        Console.WriteLine(foo7.IsNull()); // true
        foo7.Item = 3.5;
        Console.WriteLine(foo7.Item); // 3.5
        Console.WriteLine(foo7.IsNull()); // false

        // var foo5 = new Foo<int>(); // Not compile
    }
}
类程序
{
静态void Main(字符串[]参数)
{
Console.WriteLine(新的Foo().IsNull());//true
Console.WriteLine(新的Foo{Item=3}.IsNull());//false
Console.WriteLine(新的Foo().IsNull());//true
Console.WriteLine(new Foo{Item=new object()}.IsNull());//false
var foo5=新的Foo();
int integer=foo5.Item;
Console.WriteLine(整数);//0
var foo6=新的Foo();
双实数=foo6.项;
Console.WriteLine(实);//0
var foo7=新的Foo();
foo7.Item=null;
Console.WriteLine(foo7.Item);//0
Console.WriteLine(foo7.IsNull());//true
foo7.项目=3.5;
Console.WriteLine(foo7.Item);//3.5
Console.WriteLine(foo7.IsNull());//false
//var foo5=new Foo();//未编译
}
}

这样的类型约束是不可能的。根据,不存在同时捕获可空类型和引用类型的约束。由于约束只能在连接中组合,因此无法通过组合创建此类约束

但是,您可以根据需要返回到无约束类型参数,因为您可以始终检查==null。如果该类型是值类型,则该检查将始终计算为false。然后,您可能会得到R#警告“可能将值类型与null进行比较”,这并不重要,只要语义适合您

另一种选择是使用

object.Equals(value, default(T))
而不是空检查,因为默认情况下(T),其中T:class总是空的。但是,这意味着您无法区分不可为null的值是否从未被显式设置或只是被设置为其默认值。

public class Foo
    public class Foo<T>
    {
        private T item;

        public Foo(T item)
        {
            this.item = item;
        }

        public bool IsNull()
        {
            return object.Equals(item, null);
        }
    }

    var fooStruct = new Foo<int?>(3);
        var b = fooStruct.IsNull();

        var fooStruct1 = new Foo<int>(3);
        b = fooStruct1.IsNull();

        var fooStruct2 = new Foo<int?>(null);
        b = fooStruct2.IsNull();

        var fooStruct3 = new Foo<string>("qqq");
        b = fooStruct3.IsNull();

        var fooStruct4 = new Foo<string>(null);
        b = fooStruct4.IsNull();
{ 私人物品; 公共食物(T项) { this.item=项目; } 公共bool IsNull() { 返回object.Equals(item,null); } } var fooStruct=新的Foo(3); var b=fooStruct.IsNull(); var fooStruct1=新的Foo(3); b=fooStruct1.IsNull(); var fooStruct2=新的Foo(null); b=fooStruct2.IsNull(); var fooStruct3=新的Foo(“qqq”); b=fooStruct3.IsNull(); var fooStruct4=新的Foo(null); b=fooStruct4.IsNull();
如果您愿意在Foo的构造函数中进行运行时检查,而不是进行编译时检查,则可以检查该类型是否为引用类型或可为null的类型,如果是,则抛出异常

我意识到只有运行时检查可能是不可接受的,但只是以防万一:

public class Foo<T>
{
    private T item;

    public Foo()
    {
        var type = typeof(T);

        if (Nullable.GetUnderlyingType(type) != null)
            return;

        if (type.IsClass)
            return;

        throw new InvalidOperationException("Type is not nullable or reference type.");
    }

    public bool IsNull()
    {
        return item == null;
    }
}

如前所述,不能对其进行编译时检查。NET中严重缺乏通用约束,不支持大多数场景

但是,我认为这是运行时检查的一个更好的解决方案。它可以在JIT编译时进行优化,因为它们都是常量

public class SomeClass<T>
{
    public SomeClass()
    {
        // JIT-compile time check, so it doesn't even have to evaluate.
        if (default(T) != null)
            throw new InvalidOperationException("SomeClass<T> requires T to be a nullable type.");

        T variable;
        // This still won't compile
        // variable = null;
        // but because you know it's a nullable type, this works just fine
        variable = default(T);
    }
}
公共类SomeClass
{
公共类()
{
//JIT编译时检查,所以它甚至不需要计算。
如果(默认值(T)!=null)
抛出新的InvalidOperationException(“SomeClass要求T是可为null的类型。”);
T变量;
//这仍然无法编译
//变量=null;
//但是,因为您知道它是一个可为null的类型,所以它可以正常工作
变量=默认值(T);
}
}
我使用

public class Foo<T> where T: struct
{
    private T? item;
}
公共类Foo其中T:struct
{
私人T?项目;
}

我遇到这个问题是为了一个更简单的情况,即想要一个可以接受任何“可为空”(引用类型或可为空)的通用静态方法,这使我无法满意地解决这个问题。因此,我提出了我自己的解决方案,它比OP提出的问题更容易解决,只需使用两个重载方法,一个采用
T
并具有约束
where T:class
,另一个采用
T?
并具有
where T:struct

然后我意识到,该解决方案也可以应用于这个问题,通过使构造函数私有(或受保护)并使用静态工厂方法,创建一个在编译时可检查的解决方案:

    //this class is to avoid having to supply generic type arguments 
    //to the static factory call (see CA1000)
    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return Foo<TFoo>.Create(value);
        }

        public static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return Foo<TFoo?>.Create(value);
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo(T value)
        {
            item = value;
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return new Foo<TFoo>(value);
        }

        internal static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return new Foo<TFoo?>(value);
        }
    }
//此类是为了避免必须提供泛型类型参数
//到静态工厂调用(参见CA1000)
公共静态类Foo
{
公共静态Foo创建(TFoo值)
其中TFoo:class
{
返回Foo.Create(value);
}
公共静态Foo创建(TFoo?值)
其中TFoo:struct
{
返回Foo.Create(value);
}
}
公开课Foo
public class Foo<T> where T: struct
{
    private T? item;
}
    //this class is to avoid having to supply generic type arguments 
    //to the static factory call (see CA1000)
    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return Foo<TFoo>.Create(value);
        }

        public static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return Foo<TFoo?>.Create(value);
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo(T value)
        {
            item = value;
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return new Foo<TFoo>(value);
        }

        internal static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return new Foo<TFoo?>(value);
        }
    }
        var foo1 = new Foo<int>(1); //does not compile
        var foo2 = Foo.Create(2); //does not compile
        var foo3 = Foo.Create(""); //compiles
        var foo4 = Foo.Create(new object()); //compiles
        var foo5 = Foo.Create((int?)5); //compiles
    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return Foo<TFoo>.Create<TFoo>();
        }

        public static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return Foo<TFoo?>.CreateNullable<TFoo>();
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo()
        {
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return new Foo<TFoo>();
        }

        internal static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return new Foo<TFoo?>();
        }
    }
        var foo1 = new Foo<int>(); //does not compile
        var foo2 = Foo.Create<int>(); //does not compile
        var foo3 = Foo.Create<string>(); //compiles
        var foo4 = Foo.Create<object>(); //compiles
        var foo5 = Foo.CreateNullable<int>(); //compiles