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

时间:2017-02-05 21:40:29

标签: c# object inheritance composition

我一直在用一些Udemy课程补充我的学校课程。我正试图理解中间C#的一个例子,我对其中一个细节感到有些困惑。我理解继承,但我对组合有点困惑(特别是将对象传递给对象以及它们被初始化的地方)。在这个例子中,有一个DbMigrator类,Installer Class,以及DbMigrator和Installer都借用的Logger类。这是Main:

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();
        }
    }
}

我理解你创建一个传递给安装程序对象的logger对象的部分,但是我没有得到新的Logger()" dbMigrator对象的构造函数,因为正在创建的记录器没有变量名。你不需要创建" var logger1 = new Logger()"在dbMigrator对象的构造函数中?我不明白你怎么能通过" 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类有一个初始化,但我认为首先需要创建一个对象。

有人可以为我清楚吗?

1 个答案:

答案 0 :(得分:3)

首先,问题摘要 - 这是如何运作的?

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

不应该这样吗?

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

一个完整详细的答案很长,所以我会尽力使它变得美观和容易。我们首先要描述表达式是什么以及它们如何在C#中工作(更广泛地说,它们如何在任何基于堆栈的语言中工作 - 幸运的是,几乎所有这些都是你听说过的!)

表达式,陈述和操作......什么?

操作:像添加,减去等等。

表达式:一堆操作1+2+31"hi!"

等常量

声明:通常一个表达式后跟一个;但类似ifwhile循环的表达式也是语句。如果一个程序是一个烹饪配方,一个声明只是其中一个步骤。

让我们快速浏览一下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()接受任意数量的构造函数参数,并将引用输出到新创建的对象。

这是重要的部分。

操作的输出总是进入堆栈。输入总是先放在堆栈上。

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

完整的stack machine超出了此范围 - 如果您对完整的详细信息感兴趣,请查看例如: this wikipedia article。快速摘要是所有基于C语言使用相同的概念。让我们重温上面的C#语句并在堆栈操作中描述它:

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

    堆栈现在只是[2]

  • 将3推入堆栈

    现在堆栈是[2,3]

  • 乘法(从堆栈中弹出两个值并将它们相乘)

    现在堆栈是[6]

  • 将1推入堆栈

    现在堆栈是[6,1]

  • 添加(从堆栈弹出两个值,将它们一起添加)

    现在堆栈是[7]

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

    堆栈现在为空

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

调用堆栈

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

的调用堆栈

原始陈述怎么样?

好吧所以希望我们已经为这部分提供了足够的理由来更有意义!所以,让我们采取原始声明:

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

首先,new操作就像任何其他方法调用一样 - 所有参数都被压入堆栈,调用该方法(将它们全部弹出堆栈)然后将返回值放在堆栈上叠加。

所以,这里描述了堆栈样式:

  • New Logger Object(在堆上创建一个Logger对象并在堆栈上放置一个引用。没有args所以它不会弹出任何东西)

    Stack现在是[记录器参考]

  • 新的DBMigrator对象(弹出1个arg,推送引用)

    Stack现在是[DBMigrator参考]

  • 存储在本地dbMigrator

    堆栈现在为空

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

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

    Stack现在是[记录器参考]

  • 存储在本地logger

    堆栈现在为空

  • 加载本地logger

    Stack现在是[记录器参考]

  • 新的DbMigrator对象

    Stack现在是[DBMigrator参考]

  • 存储在本地dbMigrator

    堆栈现在为空

你刚学到的东西:它们都有效,但第二个稍微慢一些 - 它会更多。看看这些堆栈操作,它似乎做了一些毫无意义的事情 - 就像你只是把东西放在文件柜中,然后立即再把它取出来!

什么时候应该在堆栈上使用本地人?

作为一个简单的说明,有些语言没有本地人,只能使用堆栈。它们可以被大大优化并且趋向于更快,但它们也更难写。当您需要多次引用时,当地人很棒:

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

如果您只使用一次,那么这也非常有效:

(new Logger()).A();

奖金回合让我们说方法A做了return this;,导致再次引用堆栈上的Logger对象。那就让我们这样做:

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

这是方法链接 - 它与函数式编程相关,并且由于它的紧凑程度而越来越常见。

摘要

局部变量不是存储事物的唯一方式 - 堆栈也是如此!您不需要跟踪堆栈 - 编译器会为您完成所有这些操作。您只需要考虑那些输入/输出值。