为什么构造函数中带有可选参数的类不能满足 new() 泛型约束?

2023-11-25

以下代码无法编译,产生“Widget 必须是具有公共无参数构造函数的非抽象类型”错误。我认为编译器拥有它需要的所有信息。这是一个错误吗?疏忽?或者是否存在某些情况下这无效?

public class Factory<T> where T : new()
{
    public T Build()
    {
        return new T();
    }
}

public class Widget
{
    public Widget(string name = "foo")
    {
        Name = name;
    }

    public string Name { get; set; }
}

public class Program
{
    public static void Main()
    {
        var widget = new Widget(); // this is valid
        var factory = new Factory<Widget>(); // compiler error
    }
}

虽然这在逻辑上应该可行,但不幸的是事实并非如此。 CLR 仍然将您的构造函数视为基于参数的构造函数。

请记住,虽然 C# 支持可选参数,但这是在编译器级别、编译时完成的。基础类型仍然只包含一个带有单个参数的构造函数。就 CLR 而言,“默认参数”被转换为属性,如下所示:

public Widget(([Optional, DefaultParameterValue("foo")] string name) { // ...

CLR 是一个多语言运行时。泛型适用于所有语言的 CLR 级别,因此在没有默认参数的语言中,约束也必须成立。语言不需要理解OptionalAttribute,也不需要理解DefaultParameterValueAttribute,因此这不能对所有语言统一工作,因此是不允许的。


Edit:

回复您的评论:

我不明白的是为什么C#编译器无法生成满足CLR的必要代码

理论上,C# 编译器团队可以让该语言生成两个单独的构造函数,而不是一个标有属性的构造函数。这可能会爆炸成许多构造函数,因为命名参数为“构造函数”(或方法的方法调用)的许多可能的组合创建了功能,尤其是当多个参数可用时。我个人很高兴他们没有这样做,因为由于生成类型中的方法和构造函数过多,这会导致混乱,这会导致公共 API 看起来与生成它的代码非常不同。采用以下构造函数:

public Widget(
          int id = 0, 
          string name = "foo", 
          float width=1.0f, 
          float height=1.0f, 
          float depth=1.0f
       ) { // ... 

如果您要在此处自动生成所有可能的组合,编译器将需要生成120这个单个构造函数的构造函数,因为有 N!可能的调用方法...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么构造函数中带有可选参数的类不能满足 new() 泛型约束? 的相关文章

随机推荐