我一直在用一些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类有一个初始化,但我认为首先需要创建一个对象。
有人可以为我清楚吗?
答案 0 :(得分:3)
首先,问题摘要 - 这是如何运作的?
var dbMigrator = new DbMigrator(new Logger());
不应该这样吗?
var logger = new Logger();
var dbMigrator = new DbMigrator(logger);
一个完整详细的答案很长,所以我会尽力使它变得美观和容易。我们首先要描述表达式是什么以及它们如何在C#中工作(更广泛地说,它们如何在任何基于堆栈的语言中工作 - 幸运的是,几乎所有这些都是你听说过的!)
操作:像添加,减去等等。
表达式:一堆操作。 1+2+3
或1
或"hi!"
声明:通常一个表达式后跟一个;
但类似if
或while
循环的表达式也是语句。如果一个程序是一个烹饪配方,一个声明只是其中一个步骤。
让我们快速浏览一下C#:
int totalPrice=1+2*3;
这是一个声明,它已分解为操作,如下所示:
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();
这是方法链接 - 它与函数式编程相关,并且由于它的紧凑程度而越来越常见。
局部变量不是存储事物的唯一方式 - 堆栈也是如此!您不需要跟踪堆栈 - 编译器会为您完成所有这些操作。您只需要考虑那些输入/输出值。