一切可以为空的 C# 泛型类型约束
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19831157/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# generic type constraint for everything nullable
提问by jkammerer
So I have this class:
所以我有这门课:
public class Foo<T> where T : ???
{
private T item;
public bool IsNull()
{
return item == null;
}
}
Now I am looking for a type constraint that allows me to use everything as type parameter that can be null
. That means all reference types, as well as all the Nullable
(T?
) types:
现在我正在寻找一种类型约束,它允许我将所有内容用作可以是null
. 这意味着所有引用类型,以及所有Nullable
( T?
) 类型:
Foo<String> ... = ...
Foo<int?> ... = ...
should be possible.
应该是可能的。
Using class
as the type constraint only allows me to use the reference types.
使用class
的类型约束,只允许我使用引用类型。
Additional Information:I am writing a pipes and filters application, and want to use a null
reference as the last item that passes into the pipeline, so that every filter can shut down nicely, do cleanup, etc...
附加信息:我正在编写一个管道和过滤器应用程序,并希望使用一个null
引用作为传入管道的最后一项,以便每个过滤器都可以很好地关闭,进行清理等...
回答by Ryszard D?egan
I don't know how to implement equivalent to ORin generics. However I can propose to use defaultkey word in order to create null for nullable types and 0 value for structures:
我不知道如何在泛型中实现等价于OR。但是,我可以建议使用默认关键字,以便为可空类型创建 null 并为结构创建 0 值:
public class Foo<T>
{
private T item;
public bool IsNullOrDefault()
{
return Equals(item, default(T));
}
}
You could also implement you version of Nullable:
您还可以实现您的 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;
}
}
Example:
例子:
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
}
}
回答by Sven Amann
Such a type constraint is not possible. According to the documentation of type constraintsthere is not constraint that captures both the nullable and the reference types. Since constraints can only be combined in a conjunction, there is no way to create such a constraint by combination.
这种类型约束是不可能的。根据类型约束的文档,没有捕获可空类型和引用类型的约束。由于约束只能以连词组合,因此无法通过组合创建这样的约束。
You can, however, for your needs fall back to an unconstraint type parameter, since you can always check for == null. If the type is a value type the check will just always evaluate to false. Then you'll possibly get the R# warning "Possible compare of value type with null", which is not critical, as long as the semantics is right for you.
但是,您可以根据需要回退到无约束类型参数,因为您始终可以检查 == null。如果类型是值类型,则检查将始终评估为 false。然后您可能会收到 R# 警告“可能将值类型与 null 进行比较”,这并不重要,只要语义适合您。
An alternative could be to use
另一种方法是使用
object.Equals(value, default(T))
instead of the null check, since default(T) where T : class is always null. This, however, means that you cannot distinguish weather a non-nullable value has never been set explicitly or was just set to its default value.
而不是空检查,因为 default(T) 其中 T : class 始终为空。但是,这意味着您无法区分从未显式设置或仅设置为其默认值的不可空值的天气。
回答by SeeSharp
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();
回答by Matthew Watson
If you are willing to make a runtime check in Foo's constructor rather than having a compile-time check, you can check if the type is not a reference or nullable type, and throw an exception if that's the case.
如果您愿意在 Foo 的构造函数中进行运行时检查而不是进行编译时检查,您可以检查该类型是否不是引用或可为空类型,如果是这种情况,则抛出异常。
I realise that only having a runtime check may be unacceptable, but just in case:
我意识到只进行运行时检查可能是不可接受的,但以防万一:
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;
}
}
Then the following code compiles, but the last one (foo3
) throws an exception in the constructor:
然后编译下面的代码,但最后一个 ( foo3
) 在构造函数中抛出异常:
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());
回答by Aidiakapi
As mentioned, you cannot have a compile-time check for it. Generic constraints in .NET are severely lacking, and do not support most scenarios.
如前所述,您无法对其进行编译时检查。.NET 中的通用约束严重缺乏,并且不支持大多数场景。
However I consider this to be a better solution for run-time checking. It can be optimized at JIT compilation time, since they're both constants.
但是,我认为这是运行时检查的更好解决方案。它可以在 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);
}
}
回答by ela
I use
我用
public class Foo<T> where T: struct
{
private T? item;
}
回答by Dave M
I ran into this issue for a simpler case of wanting a generic static method that could take anything "nullable" (either reference types or Nullables), which brought me to this question with no satisfactory solution. So I came up with my own solution which was relatively easier to solve than the OP's stated question by simply having two overloaded methods, one that takes a T
and has the constraint where T : class
and another that takes a T?
and has where T : struct
.
我遇到了这个问题,因为它需要一个可以采用任何“可空”(引用类型或可空值)的通用静态方法,这让我遇到了这个问题,但没有令人满意的解决方案。所以我想出了我自己的解决方案,它比 OP 提出的问题更容易解决,只需使用两个重载方法,一个采用 aT
并具有约束where T : class
,另一个采用 aT?
并具有where T : struct
。
I then realized, that solution can also be applied to this problem to create a solution that is checkable at compile time by making the constructor private (or protected) and using a static factory method:
然后我意识到,该解决方案也可以应用于此问题,通过将构造函数设为私有(或受保护)并使用静态工厂方法来创建可在编译时检查的解决方案:
//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);
}
}
Now we can use it like this:
现在我们可以这样使用它:
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
If you want a parameterless constructor, you won't get the nicety of overloading, but you can still do something like this:
如果你想要一个无参数的构造函数,你不会得到重载的好处,但你仍然可以做这样的事情:
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?>();
}
}
And use it like this:
并像这样使用它:
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
There are few disadvantages to this solution, one is that you may prefer using 'new' to construct objects. Another is that you won't be able to use Foo<T>
as a generic type argument for a type constraint of something like: where TFoo: new()
. Finally is the bit of extra code you need here which would increase especially if you need multiple overloaded constructors.
这种解决方案有几个缺点,一个是您可能更喜欢使用“new”来构造对象。另一个原因是,你将无法使用Foo<T>
作为的类似类型约束泛型类型参数:where TFoo: new()
。最后是您在此处需要的一些额外代码,特别是如果您需要多个重载构造函数时,这些代码会增加。