.NET 中的隐式链构造函数
Implicit chain constructor in .NET
为什么没有到基 class 的隐式链构造函数?
我的意思是什么?让我们看看下面的classes:
class Person
{
public String Name { get; set; }
public Person(string name)
{
Name = name;
}
}
class EnhancedPerson : Person
{
public int Age { get; set; }
}
如果您尝试编译此代码,它将无法编译。如您所见,Person
依赖于一个名称并明确表示,您不会在没有 Name
.
的情况下构造 Person
为什么没有隐式 EnhancedPerson
构造函数,它为每个 Person
构造函数链接到基础构造函数?
只是为了争论:我开始问自己这个问题创建具有可注入依赖项的控制器,因为其中一些依赖项存在于我的所有控制器中(并且我想明确表示它们是必需的),我创建了一个基础class 而且我发现重新创建所有构造函数非常烦人...
P.s。我确实知道还有其他一些编程模式,例如具有 public 无参数构造函数和 public 依赖属性,但是......那是另一回事了。
我不是在寻找重新实现它的方法,我只是想了解为什么它不可能或为什么没有完成。
"real"原因只能由定义语言的人回答,但我可以推测...
有多种编译时错误可以自动解决,例如:
int mul(int c, int n) {
int total = 0;
for(i = 0; i < n; i++) {
total += c;
return total
}
上面的代码有 3 个错误,有 3 个明显的解决方案:
i
未定义
编译器可以很容易地将 i
推断为 int
类型并且是 for
语句的局部类型。
for
块缺少右大括号
编译器可以 "be smart about it" 并且知道缺少的括号在 return ...
语句之前,仅仅因为将 return
作为 for
块的一部分而没有任何分支是没有意义的。
return total
少了一个分号
编译时显然简单到"auto-correct"。
那么为什么编译器不这样做呢?为什么这在任何语言中都没有发生?
好吧,原因很简单,就是没有正当理由很危险。是的,这些看起来都是对小错误或忽略细节的合理解决方案,但实际上让编译器 iron-out 这些类型的问题可能会导致很多无法解释的行为(如果开发人员真的想要 return 在第一次迭代之后?)。另一方面,不解决这些问题并提醒他们的成本非常低。它让开发人员确切地知道出了什么问题,如果解决方案真的那么简单,那么手动更正应该不会花费更多时间。
这里的trade-off还是比较容易选择的,给开发者提供足够的信息,这样才能快速解决问题,万无一失!
这同样适用于问题中建议的 "implicit inherited constructor"...是的,编译器可以很容易地做到这一点...但是如果开发人员真的想在构造函数中做一些实际工作怎么办,但是忘了?编译器不应该通过编译错误来协助吗?
可能是我太天真了,但这是我的理解
示例 1
class Person
{
public String Name { get; set; }
public Person(string name)
{
Name = name;
}
}
示例 1 表示创建 Person
对象的唯一有效方法(根据 Person
class 的作者)通过提供 Name
示例 2
class EnhancedPerson : Person
{
public int Age { get; set; }
}
样本2表示EnhancedPerson
继承了Person
的所有数据。
这就是我想象编译器在遇到第一次使用时尝试构建该代码的方式
Let's create an EnhancedPerson object. Well, I need to create a Person
object for that due to inheritance. OK, so let's create a Person
object then. Well, the only valid way to create a Person is by
providing the name. So, let's check if EnhancedPerson tells me what
should I feed the constructor for Person. Shoot, it doesn't. Stop with
an error
总而言之:要构建 EnhancedPerson
,您需要构建 Person
,只有提供 Name
有 2 个常用修复程序:
- 向
Person
添加默认构造函数,以便编译器可以创建"default initialized" 对象。 请记住,如果 class 不包含任何其他构造函数,编译器仅提供一个默认构造函数。
示例 3
public Person() {
}
- 向
EnhancedPerson
添加一个构造函数,它可以为 Person
sub-object 提供 Name
。下面是几种方法。
示例 4
class EnhancedPerson : Person {
public int Age { get; set; }
public EnhancedPerson(string name) : base(name) { }
}
class EnhancedPerson : Person {
public int Age { get; set; }
public EnhancedPerson() : base(null) { }
}
注意: 编译器无法自动将 Name
的值推断为 null(因为 Person
构造函数明确请求反对它。
演示编译器猜测问题的示例
在下面的示例中,假设 txt
包含字符串形式的年龄值。你可以看到如果编译器自动将它传递给 Person
,我们可能会遇到什么灾难,因为基 class 只有一个构造函数接受一个字符串,而 EnhancedPerson
没有任何字符串属性。即使具有所有智慧的人必须 hand compile
它(EnhancedPerson
的作者除外)他们也无法判断 txt
是否必须传递给 Person
构造函数
示例 5
class EnhancedPerson : Person {
public int Age { get; set; }
public EnhancedPerson(string txt) {
}
}
我希望现在很明显为什么编译器不能自动推断当基础 class 构造函数 需要 参数时如何链接构造函数。不同的语言提供了不同的机制来指定需要将哪个参数传递给基本构造函数。
假设您有一个带有两个(参数化)构造函数的基 class。您的继承 class 不希望使用这些构造函数之一。
您现在需要发明 新 语法才能说 "don't let them use this constructor"。而 "let me define a constructor, and as part of that, choose which base class constructor to call" 是您希望在该语言中使用的普遍有用的结构。
为什么没有到基 class 的隐式链构造函数?
我的意思是什么?让我们看看下面的classes:
class Person
{
public String Name { get; set; }
public Person(string name)
{
Name = name;
}
}
class EnhancedPerson : Person
{
public int Age { get; set; }
}
如果您尝试编译此代码,它将无法编译。如您所见,Person
依赖于一个名称并明确表示,您不会在没有 Name
.
Person
为什么没有隐式 EnhancedPerson
构造函数,它为每个 Person
构造函数链接到基础构造函数?
只是为了争论:我开始问自己这个问题创建具有可注入依赖项的控制器,因为其中一些依赖项存在于我的所有控制器中(并且我想明确表示它们是必需的),我创建了一个基础class 而且我发现重新创建所有构造函数非常烦人...
P.s。我确实知道还有其他一些编程模式,例如具有 public 无参数构造函数和 public 依赖属性,但是......那是另一回事了。
我不是在寻找重新实现它的方法,我只是想了解为什么它不可能或为什么没有完成。
"real"原因只能由定义语言的人回答,但我可以推测...
有多种编译时错误可以自动解决,例如:
int mul(int c, int n) {
int total = 0;
for(i = 0; i < n; i++) {
total += c;
return total
}
上面的代码有 3 个错误,有 3 个明显的解决方案:
i
未定义
编译器可以很容易地将i
推断为int
类型并且是for
语句的局部类型。for
块缺少右大括号
编译器可以 "be smart about it" 并且知道缺少的括号在return ...
语句之前,仅仅因为将return
作为for
块的一部分而没有任何分支是没有意义的。return total
少了一个分号
编译时显然简单到"auto-correct"。
那么为什么编译器不这样做呢?为什么这在任何语言中都没有发生?
好吧,原因很简单,就是没有正当理由很危险。是的,这些看起来都是对小错误或忽略细节的合理解决方案,但实际上让编译器 iron-out 这些类型的问题可能会导致很多无法解释的行为(如果开发人员真的想要 return 在第一次迭代之后?)。另一方面,不解决这些问题并提醒他们的成本非常低。它让开发人员确切地知道出了什么问题,如果解决方案真的那么简单,那么手动更正应该不会花费更多时间。
这里的trade-off还是比较容易选择的,给开发者提供足够的信息,这样才能快速解决问题,万无一失!
这同样适用于问题中建议的 "implicit inherited constructor"...是的,编译器可以很容易地做到这一点...但是如果开发人员真的想在构造函数中做一些实际工作怎么办,但是忘了?编译器不应该通过编译错误来协助吗?
可能是我太天真了,但这是我的理解
示例 1
class Person
{
public String Name { get; set; }
public Person(string name)
{
Name = name;
}
}
示例 1 表示创建 Person
对象的唯一有效方法(根据 Person
class 的作者)通过提供 Name
示例 2
class EnhancedPerson : Person
{
public int Age { get; set; }
}
样本2表示EnhancedPerson
继承了Person
的所有数据。
这就是我想象编译器在遇到第一次使用时尝试构建该代码的方式
Let's create an EnhancedPerson object. Well, I need to create a Person object for that due to inheritance. OK, so let's create a Person object then. Well, the only valid way to create a Person is by providing the name. So, let's check if EnhancedPerson tells me what should I feed the constructor for Person. Shoot, it doesn't. Stop with an error
总而言之:要构建 EnhancedPerson
,您需要构建 Person
,只有提供 Name
有 2 个常用修复程序:
- 向
Person
添加默认构造函数,以便编译器可以创建"default initialized" 对象。 请记住,如果 class 不包含任何其他构造函数,编译器仅提供一个默认构造函数。
示例 3
public Person() {
}
- 向
EnhancedPerson
添加一个构造函数,它可以为Person
sub-object 提供Name
。下面是几种方法。
示例 4
class EnhancedPerson : Person {
public int Age { get; set; }
public EnhancedPerson(string name) : base(name) { }
}
class EnhancedPerson : Person {
public int Age { get; set; }
public EnhancedPerson() : base(null) { }
}
注意: 编译器无法自动将 Name
的值推断为 null(因为 Person
构造函数明确请求反对它。
演示编译器猜测问题的示例
在下面的示例中,假设 txt
包含字符串形式的年龄值。你可以看到如果编译器自动将它传递给 Person
,我们可能会遇到什么灾难,因为基 class 只有一个构造函数接受一个字符串,而 EnhancedPerson
没有任何字符串属性。即使具有所有智慧的人必须 hand compile
它(EnhancedPerson
的作者除外)他们也无法判断 txt
是否必须传递给 Person
构造函数
示例 5
class EnhancedPerson : Person {
public int Age { get; set; }
public EnhancedPerson(string txt) {
}
}
我希望现在很明显为什么编译器不能自动推断当基础 class 构造函数 需要 参数时如何链接构造函数。不同的语言提供了不同的机制来指定需要将哪个参数传递给基本构造函数。
假设您有一个带有两个(参数化)构造函数的基 class。您的继承 class 不希望使用这些构造函数之一。
您现在需要发明 新 语法才能说 "don't let them use this constructor"。而 "let me define a constructor, and as part of that, choose which base class constructor to call" 是您希望在该语言中使用的普遍有用的结构。