不需要将对象名称传递给构造函数吗? "new Class()" 还不是对象
Don't you need an object name to pass into the constructor? "new Class()" isn't an object yet
我一直在用一些 Udemy 课程来补充我的学校课程。我现在正试图理解一个用于中级 C# 的示例,但我对其中一个细节有点困惑。我理解继承,但我对组合有点困惑(特别是将对象传递给对象以及它们在何处初始化)。在此示例中,有一个 DbMigrator class、Installer Class 和一个 DbMigrator 和 Installer 都借用的 Logger class。这是主要内容:
namespace Composition
{
class Program
{
static void Main(string[] args)
{
var dbMigrator = new DbMigrator(new Logger());
var logger = new Logger();
var installer = new Installer(logger);
dbMigrator.Migrate();
installer.Install();
}
}
}
我理解您创建传递给安装程序对象的记录器对象的部分,但我没有得到 dbMigrator 对象的 "new Logger()" 构造函数,因为记录器没有变量名正在创建。您不需要在 dbMigrator 对象的构造函数中创建 "var logger1 = new Logger()" 吗?我不明白你怎么能在没有变量名的情况下传递 "new logger()"。
这是 DbMigrator class:
namespace Composition
{
public class DbMigrator
{
private readonly Logger _logger;
public DbMigrator(Logger logger)
{
_logger = logger;
}
public void Migrate()
{
_logger.log = ("Blah blah blah");
}
}
}
我知道 DbMigrator class 已经初始化了一个,但我认为需要先创建一个对象。
有人可以帮我解决这个问题吗?
首先,总结一下这个问题 - 这是怎么回事?
var dbMigrator = new DbMigrator(new Logger());
不应该是这个吗?
var logger = new Logger();
var dbMigrator = new DbMigrator(logger);
一个完整详细的答案很长,所以我会尽力让它变得漂亮且易于理解。为此,我们将首先描述什么是 expression 以及它们如何在 C# 中工作(更广泛地说,它们如何在任何基于堆栈的语言中工作 - 幸运的是,这几乎是您所使用的所有语言)我听说过!)
表达式、语句和操作..什么?
一个运算:像加减法等
一个表达式:一堆操作。 1+2+3
或只是一个常量,如 1
或 "hi!"
一个语句: 通常一个表达式后跟一个 ;
但类似 if
或 while
循环的东西也是语句。如果一个程序是一个烹饪食谱,那么语句只是其中的一个步骤。
让我们在 C# 中快速浏览一下:
int totalPrice=1+2*3;
这是一个语句,它被分解为 操作 ,如下所示:
- 2乘以3
- 加1
- 将结果设置到名为
totalPrice
的局部变量中
此时要问的一个很好的问题是:
所以在Multiply 2 by 3
运行之后,6存储在哪里?
答案:堆栈。稍后会详细介绍!
关于方法的快速补充说明
像Hello("all!");
这样的方法调用接受表达式作为参数。 Hello(1+1;);
失败,因为那是那里的声明。 Hello(1+(2*3));
不过还好。
操作
每个操作接受任意数量的操作数,然后可选地输出一些东西。例如,乘法运算接受 2 个操作数 - A * B - 然后输出它们相乘后的结果。 new Something()
接受任意数量的构造函数参数并输出一个 reference 到新创建的对象。
这是重要的部分。
操作的输出总是进入堆栈。输入也总是先入栈。
那么这个堆栈是什么东西?
满满的stack machine is out of scope here - if you're interested in the full details then check out e.g. this wikipedia article。快速总结是所有基于 C 的语言都使用相同的概念。让我们重新审视上面的 C# 语句并在堆栈操作中描述它:
int totalPrice=1+2*3;
将 2 压入堆栈
堆栈现在只有 [2]
将 3 压入堆栈
堆栈现在是 [2,3]
乘法(从堆栈中弹出两个值并将它们相乘)
堆栈现在是 [6]
将 1 压入堆栈
堆栈现在是[6,1]
Add(从堆栈中弹出两个值,将它们相加)
堆栈现在是 [7]
Store in local totalPrice
(弹出栈值并存储)
堆栈现在是空的
旁注:这些堆栈操作是 C# 编译器生成的。叫做CIL or .NET IL.
调用栈
健康警告!调用堆栈完全不同。是可以"overflow".
的调用栈
原来的说法呢?
好的,希望我们已经涵盖了足够多的内容,让这部分内容更有意义!那么我们就拿原文来说吧:
var dbMigrator = new DbMigrator(new Logger());
首先,new
操作就像任何其他方法调用一样 - 所有参数都被压入堆栈,方法被调用(将它们全部弹出堆栈)并且 return然后将值放入堆栈。
所以,这里描述的是堆栈样式:
新建 Logger 对象(在堆上创建一个 Logger 对象并将对它的引用放在堆栈上。没有参数,所以它不会弹出任何东西)
堆栈现在是[记录器参考]
新 DBMigrator 对象(弹出 1 arg,推送引用)
堆栈现在是 [DBMigrator 参考]
存储在本地dbMigrator
堆栈现在为空
要真正巩固这一点,请将其与替代方案(也有效)进行比较:
var logger = new Logger();
var dbMigrator = new DbMigrator(logger);
新建记录器对象
堆栈现在是[记录器参考]
存储在本地logger
堆栈现在为空
加载本地logger
堆栈现在是[记录器参考]
新建 DbMigrator 对象
堆栈现在是 [DBMigrator 参考]
存储在本地dbMigrator
堆栈现在是空的[=225=
您刚刚学到的内容: 它们都可以工作,但第二个稍微慢一些 - 它 更多 。看那些栈操作,好像在做一些完全没有意义的事情——就像你把东西放在文件柜里,然后马上又拿出来一样!
什么时候应该在堆栈上使用局部变量?
顺便提一句,有些语言没有本地语言,只使用堆栈。它们可以被极大地优化并且往往运行得更快,但它们也更难编写。当您需要多次引用某些内容时,本地人很棒:
Logger logger=new Logger();
// Using it twice!
logger.A();
logger.B();
如果您只使用过一次,那么这也完全有效:
(new Logger()).A();
奖金回合: 假设方法 A
确实 return this;
导致再次引用堆栈上的 Logger 对象。这会让我们这样做:
(new Logger()).A().B();
这是方法链接 - 它与函数式编程相关,并且由于它的紧凑性而变得越来越普遍。
总结
局部变量并不是唯一的存储方式——堆栈也是如此!您不需要跟踪堆栈 - 编译器会为您完成所有这些工作。你只需要考虑那些输入/输出值。
我一直在用一些 Udemy 课程来补充我的学校课程。我现在正试图理解一个用于中级 C# 的示例,但我对其中一个细节有点困惑。我理解继承,但我对组合有点困惑(特别是将对象传递给对象以及它们在何处初始化)。在此示例中,有一个 DbMigrator class、Installer Class 和一个 DbMigrator 和 Installer 都借用的 Logger class。这是主要内容:
namespace Composition
{
class Program
{
static void Main(string[] args)
{
var dbMigrator = new DbMigrator(new Logger());
var logger = new Logger();
var installer = new Installer(logger);
dbMigrator.Migrate();
installer.Install();
}
}
}
我理解您创建传递给安装程序对象的记录器对象的部分,但我没有得到 dbMigrator 对象的 "new Logger()" 构造函数,因为记录器没有变量名正在创建。您不需要在 dbMigrator 对象的构造函数中创建 "var logger1 = new Logger()" 吗?我不明白你怎么能在没有变量名的情况下传递 "new logger()"。
这是 DbMigrator class:
namespace Composition
{
public class DbMigrator
{
private readonly Logger _logger;
public DbMigrator(Logger logger)
{
_logger = logger;
}
public void Migrate()
{
_logger.log = ("Blah blah blah");
}
}
}
我知道 DbMigrator class 已经初始化了一个,但我认为需要先创建一个对象。
有人可以帮我解决这个问题吗?
首先,总结一下这个问题 - 这是怎么回事?
var dbMigrator = new DbMigrator(new Logger());
不应该是这个吗?
var logger = new Logger();
var dbMigrator = new DbMigrator(logger);
一个完整详细的答案很长,所以我会尽力让它变得漂亮且易于理解。为此,我们将首先描述什么是 expression 以及它们如何在 C# 中工作(更广泛地说,它们如何在任何基于堆栈的语言中工作 - 幸运的是,这几乎是您所使用的所有语言)我听说过!)
表达式、语句和操作..什么?
一个运算:像加减法等
一个表达式:一堆操作。 1+2+3
或只是一个常量,如 1
或 "hi!"
一个语句: 通常一个表达式后跟一个 ;
但类似 if
或 while
循环的东西也是语句。如果一个程序是一个烹饪食谱,那么语句只是其中的一个步骤。
让我们在 C# 中快速浏览一下:
int totalPrice=1+2*3;
这是一个语句,它被分解为 操作 ,如下所示:
- 2乘以3
- 加1
- 将结果设置到名为
totalPrice
的局部变量中
此时要问的一个很好的问题是:
所以在Multiply 2 by 3
运行之后,6存储在哪里?
答案:堆栈。稍后会详细介绍!
关于方法的快速补充说明
像Hello("all!");
这样的方法调用接受表达式作为参数。 Hello(1+1;);
失败,因为那是那里的声明。 Hello(1+(2*3));
不过还好。
操作
每个操作接受任意数量的操作数,然后可选地输出一些东西。例如,乘法运算接受 2 个操作数 - A * B - 然后输出它们相乘后的结果。 new Something()
接受任意数量的构造函数参数并输出一个 reference 到新创建的对象。
这是重要的部分。
操作的输出总是进入堆栈。输入也总是先入栈。
那么这个堆栈是什么东西?
满满的stack machine is out of scope here - if you're interested in the full details then check out e.g. this wikipedia article。快速总结是所有基于 C 的语言都使用相同的概念。让我们重新审视上面的 C# 语句并在堆栈操作中描述它:
int totalPrice=1+2*3;
将 2 压入堆栈
堆栈现在只有 [2]
将 3 压入堆栈
堆栈现在是 [2,3]
乘法(从堆栈中弹出两个值并将它们相乘)
堆栈现在是 [6]
将 1 压入堆栈
堆栈现在是[6,1]
Add(从堆栈中弹出两个值,将它们相加)
堆栈现在是 [7]
Store in local
totalPrice
(弹出栈值并存储)堆栈现在是空的
旁注:这些堆栈操作是 C# 编译器生成的。叫做CIL or .NET IL.
调用栈
健康警告!调用堆栈完全不同。是可以"overflow".
的调用栈原来的说法呢?
好的,希望我们已经涵盖了足够多的内容,让这部分内容更有意义!那么我们就拿原文来说吧:
var dbMigrator = new DbMigrator(new Logger());
首先,new
操作就像任何其他方法调用一样 - 所有参数都被压入堆栈,方法被调用(将它们全部弹出堆栈)并且 return然后将值放入堆栈。
所以,这里描述的是堆栈样式:
新建 Logger 对象(在堆上创建一个 Logger 对象并将对它的引用放在堆栈上。没有参数,所以它不会弹出任何东西)
堆栈现在是[记录器参考]
新 DBMigrator 对象(弹出 1 arg,推送引用)
堆栈现在是 [DBMigrator 参考]
存储在本地
dbMigrator
堆栈现在为空
要真正巩固这一点,请将其与替代方案(也有效)进行比较:
var logger = new Logger();
var dbMigrator = new DbMigrator(logger);
新建记录器对象
堆栈现在是[记录器参考]
存储在本地
logger
堆栈现在为空
加载本地
logger
堆栈现在是[记录器参考]
新建 DbMigrator 对象
堆栈现在是 [DBMigrator 参考]
存储在本地
dbMigrator
堆栈现在是空的[=225=
您刚刚学到的内容: 它们都可以工作,但第二个稍微慢一些 - 它 更多 。看那些栈操作,好像在做一些完全没有意义的事情——就像你把东西放在文件柜里,然后马上又拿出来一样!
什么时候应该在堆栈上使用局部变量?
顺便提一句,有些语言没有本地语言,只使用堆栈。它们可以被极大地优化并且往往运行得更快,但它们也更难编写。当您需要多次引用某些内容时,本地人很棒:
Logger logger=new Logger();
// Using it twice!
logger.A();
logger.B();
如果您只使用过一次,那么这也完全有效:
(new Logger()).A();
奖金回合: 假设方法 A
确实 return this;
导致再次引用堆栈上的 Logger 对象。这会让我们这样做:
(new Logger()).A().B();
这是方法链接 - 它与函数式编程相关,并且由于它的紧凑性而变得越来越普遍。
总结
局部变量并不是唯一的存储方式——堆栈也是如此!您不需要跟踪堆栈 - 编译器会为您完成所有这些工作。你只需要考虑那些输入/输出值。