首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >所有可以为空的内容的C#泛型类型约束

所有可以为空的内容的C#泛型类型约束
EN

Stack Overflow用户
提问于 2013-11-07 16:34:14
回答 6查看 60.4K关注 0票数 131

所以我有这样一个类:

代码语言:javascript
复制
public class Foo<T> where T : ???
{
    private T item;

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

}

现在我正在寻找一种类型约束,它允许我将所有内容都用作可以为null的类型参数。这意味着所有引用类型,以及所有Nullable (T?)类型:

代码语言:javascript
复制
Foo<String> ... = ...
Foo<int?> ... = ...

应该是可能的。

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

附加信息:我正在编写一个管道和过滤器应用程序,并希望使用null引用作为传递到管道的最后一项,以便每个过滤器可以很好地关闭,做清理等…

EN

回答 6

Stack Overflow用户

发布于 2013-11-07 17:02:02

如果您希望在Foo的构造函数中进行运行时检查,而不是进行编译时检查,则可以检查该类型是否不是引用类型或可空类型,如果是,则抛出异常。

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

代码语言:javascript
复制
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;
    }
}

然后编译以下代码,但最后一段代码(foo3)在构造函数中抛出异常:

代码语言:javascript
复制
var foo1 = new Foo<int?>();
Console.WriteLine(foo1.IsNull());

var foo2 = new Foo<string>();
Console.WriteLine(foo2.IsNull());

var foo3= new Foo<int>();  // THROWS
Console.WriteLine(foo3.IsNull());
票数 25
EN

Stack Overflow用户

发布于 2017-04-11 23:38:05

我遇到这个问题是因为我想要一个可以接受任何“可空”(引用类型或nullable)的泛型静态方法的简单情况,这给我带来了这个问题,但没有令人满意的解决方案。因此,我想出了我自己的解决方案,这比简单地使用两个重载方法来解决OP的问题要容易得多,一个重载方法接受T并具有约束where T : class,另一个接受T?并具有where T : struct

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

代码语言:javascript
复制
    //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);
        }
    }

现在我们可以这样使用它:

代码语言:javascript
复制
        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

如果你想要一个无参数的构造器,你不会得到重载的精确度,但你仍然可以这样做:

代码语言:javascript
复制
    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?>();
        }
    }

并像这样使用它:

代码语言:javascript
复制
        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

这种解决方案的缺点很少,其中之一是您可能更喜欢使用“new”来构造对象。另一个原因是您不能将Foo<T>用作诸如:where TFoo: new()之类的类型约束的泛型类型参数。最后是你需要的额外代码,特别是当你需要多个重载的构造器时。

票数 19
EN

Stack Overflow用户

发布于 2015-03-15 00:27:36

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

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

代码语言:javascript
复制
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);
    }
}
票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19831157

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档