结合struct和new()泛型类型约束

6

最近我阅读了Nullable文档,发现Nullable的定义如下:

public struct Nullable<T> where T : struct, new()

我曾经误解认为结构体总是有一个公共的无参构造函数,如果这是正确的,那么new()类型约束在这里起到了什么作用?

正如我在回答中所暗示的那样,Nullable<T> 实际上具有约束 where T : struct, ValueType, new(),尽管这种额外的冗余似乎目前并没有增加任何东西。(即 IL 为 Nullable`1<valuetype .ctor (System.ValueType) T> - Mark Hurd
3个回答

3

对于结构体,使用new没有意义。但对于类来说则有意义。

在您的情况下,这是多余的。

public T FactoryCreateInstance<T>() where T : new()
{
return new T();
}

在上述情况下,指定新的约束是有意义的,但当它已经被限制为结构时,则无需进一步指定。

对于值类型来说,无参构造函数是C#的限制而不是CLI的限制。也许这就是为什么会重复指定以留出一些余地。


你认为为什么new()不适用于结构体?根据我在答案中提供的引用和使用new关键字对结构体进行维度划分的事实,我认为这不会有任何区别。不过我还没有尝试过,所以我只是好奇你是否确切知道这一点? - Brandon Moore
我尝试过了,只需要使用结构体(而不是new())就足以在泛型中引用像x = new T()这样的代码。 - hatchet - done with SOverflow
存在一个什么也不做的默认无参构造函数并不是CLI的限制,但当CLI为结构体分配空间时,它不会调用其中任何元素的任何构造函数。微软可能认为,由于可以使用实例成员来完成构造函数可以完成的任何操作,因此后者并不是很必要。他们可能大多数情况下是正确的,除了在涉及泛型的某些情况下,构造函数可能能够提供稍微更好的性能。 - supercat

1

只是注意到这是有效的、可验证的IL(即。

 .class public sequential ansi sealed StructNewStruct`1<valuetype .ctor ([mscorlib]System.ValueType) T>
     extends [mscorlib]System.ValueType

编译,就像更简单的一样

 .class public sequential ansi sealed StructNewStruct`1<valuetype .ctor T>
     extends [mscorlib]System.ValueType

)但我还没有针对这些内容编写任何不同的代码,只是使用简单的where T:struct(或VB.NET中的(Of T As Structure)和IL中的<valuetype T>)提供。

具体来说,对于任何带有简单struct约束的泛型参数,已经不允许使用Nullable结构体。(除了存储之外,它似乎对于几乎所有目的都是类。)

因此,总之,Nullable<T>的当前(等效于)where T:ValueType, struct, new()似乎当前与where T:struct完全相同。

提供信息,我使用了更新的DotLisp来创建通用类型(只需使用MakeGenericType),尝试为4.0框架中所有程序集中的所有类型创建StructNewStruct<t>StructStruct<t>(*)类型,而无需尝试加载“不寻常”的程序集(例如可能未加载System.Web)。 (如果在“晦涩”的框架程序集中有任何“特殊”类型,请告诉我,我将确保它们被加载并尝试。)所有类型都使用相同的结构成功或失败。

(*) StructStruct<T> where T:struct


可空类型的行为与类完全不同,除了编译器允许将 null 隐式转换为任何非可空值类型 T 的默认值 Nullable<T> 外,可空类型(令人烦恼的是)不满足结构约束。请注意,将非 null 类型存储位置强制转换为接口类型只会重新解释对现有对象的引用,但每次将非 null 的 Nullable<T> 强制转换为接口类型都会产生一个新的堆类型对象,该堆类型对应于 T - supercat
@supercat 或许我稍微夸大了 Nullable<s> 的类似性,因为它不满足 struct 约束条件。请注意,Nullable<> 本身没有实现 任何 接口,因此总是需要进行转换。 - Mark Hurd
1
我得出的结论是额外的约束没有添加任何东西,但也没有削弱任何内容,这就是编译器团队包含它的原因,以便在未来能够更灵活一些。好答案,谢谢 +1。 - Rich O'Kelly
@MarkHurd:尽管编译器不会明确允许在同一泛型类型上直接指定new约束和struct约束,但如果将struct约束应用于间接具有new约束的类型(通过被限制为具有该约束的泛型类型),则编译器不会报错。我认为结构体能够满足对具有new约束的泛型类型的约束的唯一方法是将结构体限制为自身(尽管经过再次思考,我并不100%确定,因为... - supercat
我知道COM对象可以声明为接口类型,但仍然可以实例化;我不认为任何这样的类型都能满足“new”约束,因为不能简单地使用“new()”来创建它们,但我不知道确切的操作方法。 - supercat
显示剩余5条评论

1

它不必具有无参数的构造函数,即使它确实拥有一个,也不必是公共的。 我相信“new()”要求它具备这两个条件。

编辑:根据MSDN文档:“new约束指定泛型类声明中的任何类型参数必须具有公共的无参数构造函数。”


1
请参阅结构体规范:结构体构造函数与类构造函数相似,但有以下区别:结构体不能包含显式的无参数构造函数。结构体成员会自动初始化为它们的默认值。 结构体不能以 base(argument-list)的形式拥有初始化器。 - Rich O'Kelly
啊...这说明我已经很久没有尝试创建一个结构体了。感谢您的纠正。+1 - Brandon Moore
@rich.okelly:结构体与类的不同之处并不在于字段在创建时是否初始化为默认值(类字段在类对象创建时初始化为默认值),而是因为由于结构体类型的存储位置结构体类型实例,因此一旦存储位置存在,结构体实例也随之存在。创建一个数组ClassType[100]不会创建任何实例,但创建一个StructType[100]会创建100个新的结构体实例。 - supercat
@supercat 我的评论旨在指出,与这个答案所述的相反,结构体总是具有公共无参构造函数。针对您的评论,是的,我知道这一点,并稍微扩展一下 - 创建一个数组ClassType [100]实际上为100个指向ClassType的指针分配内存,而数组StructType [100]将为100个StructType分配内存。您所说的是正确的,因为为结构体分配内存等同于创建结构体。 - Rich O'Kelly
@rich.okelly:结构体总是有一个公共的无参数构造函数可以被调用,但是在C#或vb.net中从来没有一个真正做任何事情的无参数构造函数。这种设计基于这样一个事实,即让default<T>成为既不是null也不是new T()会令人困惑。我不知道CLS规范是否要求语言提供一种显式调用外部定义的结构体的无参数构造函数的方法,因为CIL中没有任何东西可以阻止结构体具有非平凡的无参数构造函数。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接