结构上的方法调用?

当我们在对象上调用方法时,对象的引用将隐式传递给方法。

所以我的问题是在结构上调用方法时会发生什么? 它与这方面的类似吗?

根据CIL规范 , this指针通过引用/作为托管指针传递。 正如Binary Worrier所假设的那样,这是一个CILfunction。

应该对类的实例和虚方法进行编码,以期望对该类的实例的引用作为该指针。 相比之下,值类型的实例和虚方法应编码为期望托管指针(请参阅分区I)到值类型的未装箱实例。 当盒装值类型作为this指针传递给虚拟方法时,CLI应将盒装值类型转换为指向未装箱值类型的托管指针,该方法的实现由未装箱的值类型提供。

因此,从高级角度来看,对引用类型(类)的实例方法的调用如下所示:

 MyClass myClass = MyClass_Constructor(); MyClass_MyInstanceMethod(myClass, myFirstParameter); // ^ // The "this" argument 

并调用值类型(struct)的实例方法,如下所示:

 MyStruct myStruct = MyStruct_Constructor(); MyStruct_MyInstanceMethod(ref myStruct, myFirstParameter); // ^ // The "this" argument 

弗洛里安是对的; 在我们谈论这个主题时,还有一些细节:

当我们在对象上调用方法时,对象的引用将隐式传递给方法。

正确。 考虑这个问题的一种方法是调用方法:

 class C { int y; public void M(int x) { Console.WriteLine(x + y); } } ... C c = new C(); cM(10); 

实际上是一样的

 class C { int y; public static void M(C _this, int x) { Console.WriteLine(x + _this.y); } } ... C c = new C(); CM(c, 10); 

也就是说,每个实例方法都有一个隐藏的“this”参数,并且该方法“真的”是静态的。

所以我的问题是在结构上调用方法时会发生什么? 它与这方面的类似吗?

是。 传递的内容是包含结构的变量的别名,而不是对实例的引用。 这就是struct方法可以改变结构的方式。 (当然,改变结构是一种不好的做法,但有时是必要的。)

 struct S { int y; public void M(int x) { Console.WriteLine(x + y); } } ... S s = new S(); sM(10); 

在逻辑上是相同的

 struct S { int y; public static void M(ref S _this, int x) { Console.WriteLine(x + _this.y); } } ... S s = new S(); SM(ref s, 10); 

然后出现一个有趣的问题:如果“接收器”不是变量怎么办? (*)您只能将ref作为变量。 假设你有:

 GetAnS().M(10); 

??? 那么会发生什么?

我们为你做一个变量。 那变成了

 S temporary = GetAnS(); temporary.M(10); 

现在接收器是一个变量,所以我们可以将“隐藏此参数”作为它的别名。

(*)这里还有其他有趣的案例,比如结构是可变的但是变量是只读的,等等。

我前段时间写了一系列关于这个主题的博客文章。 特别是, http://www.simple-talk.com/community/blogs/simonc/archive/2010/11/02/95489.aspx

在IL中,您可以将托管指针存储在堆栈上,这类似于对象引用,除了它可以指向除对象引用之外的其他内容。 有一些特定的指令可以获取指向某个东西的托管指针,例如ldloca ,它获取指向局部变量的指针,或ldelema ,它获取指向数组中特定元素的指针。

这些是安全指针,因为垃圾收集器知道它们,因此如果您指向的对象被GC移动,则更改它们。

对结构的方法调用需要一个指向结构的托管指针(可以在内存中的任何位置 – 在堆栈上,在参数列表中,在堆上的另一个对象中),该方法将作为this指针执行。 对引用类型的方法调用需要对象引用。

在generics类型上调用方法时,这会导致一些问题,这些方法可以是值类型或引用类型。 我在后面的博客文章中看一下。

能够使用托管指针到值类型的副作用之一是IL可以在盒装值类型上调用方法而不必将其取消装箱:

 // you have a struct on the stack box MyStructType // this copies the value on the stack to an object on the heap unbox MyStructType // this returns a managed pointer to the value type instance in its boxed form. It doesn't copy the value itself back to the stack (despite the name) call instance void MyStructType::MyMethod()