未分配类的调用方法 [英] Calling method of non-assigned class

查看:121
本文介绍了未分配类的调用方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对这两个方面都有疑问;

I have doubt about these two aspects;

第一个;

        Test test = new Test();

        result = test.DoWork(_param);


第二个;


Second one;

       result = new Test().DoWork(_param);

如果我们不将新创建的实例分配给变量并直接调用该方法怎么办?

What happens if we dont assign the newly created instance to a variable and directly call the method?

我看到两种方式在IL代码上有一些区别.

I see some difference between two way on IL code.

下面的这是第一个c#代码的IL输出

This one below is IL output of first c# code

 IL_0000:  ldstr      "job "
 IL_0005:  stloc.0
 IL_0006:  newobj     instance void Works.Test::.ctor()
 IL_000b:  stloc.1
 IL_000c:  ldloc.1
 IL_000d:  ldloc.0
 IL_000e:  callvirt   instance string Works.Test::DoWork(string)
 IL_0013:  pop
 IL_0014:  ret


这是第二个C#代码的IL输出


And this one is the IL output of the second c# code

 IL_0000:  ldstr      "job "
 IL_0005:  stloc.0
 IL_0006:  newobj     instance void Works.Test::.ctor()
 IL_000b:  ldloc.0
 IL_000c:  call       instance string Works.Test::DoWork(string)
 IL_0011:  pop
 IL_0012:  ret

能告诉我吗?

推荐答案

在这里很难找到问题,但我想您要问的是:

The question is a little bit hard to find here but I think what you are asking is:

为什么将新创建的引用分配给变量会导致编译器生成callvirt,但是直接调用该方法会生成调用?

why does assigning the newly created reference to a variable cause the compiler to generate a callvirt, but calling the method directly generates a call?

您非常观察到这种细微的差别.

You are very observant to notice this subtle difference.

在回答您的问题之前,让我们先回答其他问题.

Before we get to your question let's answer some other questions.

我应该相信编译器会生成良好的代码吗?

Should I trust that the compiler generates good code?

通常是.偶尔会有代码生成错误,但这不是其中之一.

Generally yes. There are occasional code gen bugs but this is not one of them.

使用callvirt调用非虚拟方法合法吗?

Is it legal to call a non-virtual method with callvirt?

是的

使用call调用虚拟方法合法吗?

Is it legal to call a virtual method with call?

是的,如果要尝试调用基类方法而不是派生类中的重写.但是通常这不会发生.

Yes, if you're trying to, say, call a base class method rather than an override in a derived class. But usually this does not happen.

此示例中调用的方法是否是虚拟的?

Is the method being called in this example virtual or not?

它不是虚拟的.

由于该方法不是虚拟的,因此可以使用callvirt或call进行调用.为什么编译器可以始终生成两次或两次都生成callvirt时,为什么有时生成callvirt并有时生成call?

Since the method is not virtual, it could be called with callvirt or call. Why does the compiler sometimes generate callvirt and sometimes generate call, when it could generate callvirt both times or call both times, consistently?

现在我们进入您问题的有趣部分.

Now we get to the interesting part of your question.

call和callvirt之间有两个区别.

There are two differences between call and callvirt.

  • 调用不执行虚拟调度; callvirt在调用它之前在虚拟函数分配表中查找正确的方法.因此,callvirt的速度大约慢了十亿分之一秒.

  • call does not do virtual dispatch; callvirt looks up the right method in the virtual function dispatch table before it calls it. So therefore callvirt is about a nanosecond slower.

callvirt 始终检查接收方是否为空,而不管所调用的方法是否为虚拟方法.呼叫不检查接收方是否为空.通过调用以"this"为空的方法是合法的.

callvirt always checks if the receiver is null, regardless of whether the method called is virtual or not. call does not check to see if the receiver is null. It is legal to call a method with a null "this" via call.

现在,您也许会看到前进的方向.

Now perhaps you see where this is going.

每当在空引用接收方上进行调用时,是否需要C#崩溃并具有空取消引用异常?

Is C# required to crash with a null dereference exception whenever a call is made on a null reference receiver?

.当您使用空接收器调用某些程序时,需要 C#崩溃.因此,C#在生成用于调用方法的代码时具有以下选择:

Yes. C# is required to crash when you invoke something with a null receiver. Therefore C# has the following choices when generating code to call a method:

  • 情况1:生成检查空值的IL,然后生成一个调用.
  • 情况2:生成呼叫病毒.
  • 情况3:生成呼叫,但不要以空检查开头.

案例1只是愚蠢的. IL较大,因此占用磁盘上更多的空间,加载速度较慢,jit速度较慢.当callvirt自动执行null检查时,生成此代码是愚蠢的.

Case 1 is just dumb. The IL is larger, and so takes up more room on disk, is slower to load, is slower to jit. It would be foolish to generate this code when callvirt automatically does a null check.

案例2很聪明. C#编译器生成callvirts,以便自动执行null检查.

Case 2 is smart. The C# compiler generates callvirts so that the null check is done automatically.

现在情况3呢?在什么情况下C#可以跳过空检查并生成调用?仅在以下情况下:

Now what about case 3? Under what circumstances can C# skip the null check and generate a call? Only when:

  • 该调用是非虚拟方法调用,并且
  • C#已经知道接收者不为空

但是C#知道new Foo().Bar()中的接收者不能为null,因为如果为null,则该构造将引发异常,而我们将永远无法进行调用!

But C# knows that in new Foo().Bar() the receiver cannot be null because if it was, then the construction would have thrown an exception and we'd never get to the call!

编译器不够聪明,无法意识到该变量仅被分配了非null值.因此,它会生成一个安全的callvirt.

The compiler is not smart enough to realize that the variable has only ever been assigned non-null values. So it generates a callvirt to be safe.

编译器可以编写得如此精巧.编译器已经必须跟踪变量的分配状态以进行确定的分配检查.它还可以跟踪被分配了可能为空的内容"状态,然后在两种情况下都将生成一个调用.但是编译器还不那么聪明.

The compiler could be written to be that smart. The compiler already has to track the assignment state of variables for definite assignment checking. It could also track the "was assigned something that might be null" state, and then it would generate a call in both cases. But the compiler is not (yet) that smart.

这篇关于未分配类的调用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆