你不需要一个对象名称来传递到构造函数中吗? "new Class()"还不是对象



我一直在用一些Udemy课程来补充我的学校课程。我现在正试图理解中间C#的一个例子,但我对其中的一个细节有点困惑。我理解继承,但我对组合(特别是将对象传递给对象以及在哪里初始化对象)有点困惑。在本例中,有一个DbMigrator类、安装程序类和一个Logger类,DbMigrator和安装程序都借用它们。这里是主要:

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对象的构造函数中创建"varlogger1=newLogger()"吗?我不明白如何在没有变量名的情况下传递"new logger()"。

这是DbMigrator类:

namespace Composition
{
public class DbMigrator
{
private readonly Logger _logger;
public DbMigrator(Logger logger)
{
_logger = logger;
}
public void Migrate()
{
_logger.log = ("Blah blah blah");
}
}
}

我意识到DbMigrator类已经初始化了一个对象,但我认为需要首先创建一个对象。

有人能帮我清理一下吗?

因此,首先,对这个问题进行总结——这是如何工作的?

var dbMigrator = new DbMigrator(new Logger());

不应该是这个吗?

var logger = new Logger();
var dbMigrator = new DbMigrator(logger);

一个完整详细的答案很长,所以我会尽我所能让它变得简单易懂。我们将首先描述表达式是什么,以及它们在C#中的工作方式(更广泛地说,它们在任何基于堆栈的语言中的工作情况-幸运的是,这几乎是你听说过的所有语言!)

表达式、语句和运算。。什么

操作:像加法、减法等

一个表达式:一堆操作。CCD_ 1或只是像1"hi!"这样的常数

语句:通常一个表达式后面跟着;,但类似ifwhile循环的内容也是语句。如果一个程序是一个烹饪食谱,那么陈述只是其中一个步骤。

让我们在C#中快速了解一下:

int totalPrice=1+2*3;

这是一个语句,它被分解为操作,如下所示:

  • 2乘3
  • 添加1
  • 将结果设置为名为totalPrice的局部变量

在这一点上,一个很好的问题是:

那么Multiply 2 by 3运行后,6存储在哪里

答案:堆栈稍后会有更多内容!

关于方法的快速附加说明

Hello("all!");这样的方法调用接受表达式作为参数。1+2+30失败了,因为这是一个语句。Hello(1+(2*3));是好的。

操作

每个操作接收任意数量的运算数,然后可选地输出一些东西。例如,乘法运算取2个操作数A*B,然后输出它们相乘后的结果。new Something()接受任意数量的构造函数参数,并向新创建的对象输出引用

这是重要的部分。

操作的输出总是进入堆栈。输入也总是先放到堆栈中

那么这个堆叠的东西是什么

全栈机器不在这里的范围内——如果你对完整的细节感兴趣,那么看看这篇维基百科文章。快速总结一下,所有基于C的语言都使用相同的概念。让我们重新审视上面的C#语句,并在堆栈操作中描述它:

int totalPrice=1+2*3;
  • 将2推到堆栈上

    堆栈现在只有[2]

  • 将3推到堆栈上

    堆栈现在是[2,3]

  • 相乘(从堆栈中弹出两个值并相乘)

    堆栈现在是[6]

  • 将1推到堆栈上

    堆栈现在是[6,1]

  • 相加(从堆栈中弹出两个值,将它们相加)

    堆栈现在是[7]

  • 存储在本地totalPrice中(弹出堆栈值并存储)

    堆栈现在为空

附带说明:这些堆栈操作是C#编译器生成的。它被称为CIL或.NET IL。

调用堆栈

健康警告调用堆栈完全不同。它是可以"溢出"的调用堆栈。

最初的声明呢

好吧,希望我们已经为这一部分做了足够的铺垫,让它更有意义!因此,让我们采用原始声明:

var dbMigrator = new DbMigrator(new Logger());

首先,new操作就像任何其他方法调用一样——所有参数都被推送到堆栈上,方法被调用(将它们全部从堆栈中弹出),然后返回值被放在堆栈上。

因此,这里描述的是堆栈样式:

  • New Logger Object(在堆上创建一个Logger对象,并在堆栈上对其进行引用。没有args,因此不会弹出任何内容)

    堆栈现在是[记录器参考]

  • 新建DBMigrator对象(关闭1 arg,推送引用)

    Stack现在是[DBMigrator参考]

  • 存储在本地dbMigrator

    堆栈现在为空

要真正巩固这一点,请将其与替代方案(也是有效的)进行比较:

var logger = new Logger();
var dbMigrator = new DbMigrator(logger);
  • 新记录器对象

    堆栈现在是[记录器参考]

  • 存储在本地logger

    堆栈现在为空

  • 加载本地logger

    堆栈现在是[记录器参考]

  • 新的DbMigrator对象

    Stack现在是[DBMigrator参考]

  • 存储在本地dbMigrator

    堆栈现在为空

你刚刚学到的:它们都能工作,但第二个稍微慢一点——它能做得更多。看着这些堆叠操作,它似乎做了一些完全没有意义的事情——就像你只是把一些东西放在文件柜里,然后立即把它再拿出来!

什么时候应该在堆栈上使用local

顺便说一下,有些语言没有本地语言,只使用堆栈。它们可以得到极大的优化,而且速度更快,但也更难编写。当你需要对某个东西进行多次引用时,本地语言非常好:

Logger logger=new Logger();
// Using it twice!
logger.A();
logger.B();

如果你只使用过一次,那么这也是完全有效的:

(new Logger()).A();

奖励回合:假设方法A执行了return this;,导致再次引用堆栈上的Logger对象。这样我们就可以做到:

(new Logger()).A().B();

这是方法链接-它与函数编程有关,并且由于其紧凑性而变得越来越常见。

摘要

局部变量并不是存储事物的唯一方式,堆栈也能做到这一点!您不需要跟踪堆栈——编译器会为您完成所有这些工作。您只需要考虑这些输入/输出值。

最新更新