当我们在对象上调用方法时,对象的引用会隐式传递给方法。
所以我的问题是在结构上调用方法时会发生什么?它与这方面的类相似吗?
答案 0 :(得分:24)
根据CIL spec,this
指针通过引用/作为托管指针传递。正如Binary Worrier所假设的那样,这是一个CIL功能。
实例和虚拟方法 类应编码为期望a 引用该类的实例 作为这个指针。 相比之下, 实例和虚拟价值方法 类型应编码为期望a 托管指针(请参阅分区I) 值类型的未装箱实例。 CLI应转换盒装值 键入到托管指针 装箱值时的未装箱值类型 type作为this指针传递给 一种实现的虚方法 由未装箱的值类型提供。
因此,从高级角度来看,对引用类型(类)的实例方法的调用如下所示:
MyClass myClass = MyClass_Constructor();
MyClass_MyInstanceMethod(myClass, myFirstParameter);
// ^
// The "this" argument
调用值类型(struct)的实例方法,如下所示:
MyStruct myStruct = MyStruct_Constructor();
MyStruct_MyInstanceMethod(ref myStruct, myFirstParameter);
// ^
// The "this" argument
答案 1 :(得分:20)
弗洛里安是对的;我们谈论这个主题时还有一些细节:
当我们在对象上调用方法时,对象的引用会隐式传递给方法。
正确。考虑这个问题的一种方法是调用方法:
class C
{
int y;
public void M(int x)
{
Console.WriteLine(x + y);
}
}
...
C c = new C();
c.M(10);
实际上与
相同class C
{
int y;
public static void M(C _this, int x)
{
Console.WriteLine(x + _this.y);
}
}
...
C c = new C();
C.M(c, 10);
也就是说,每个实例方法都有一个隐藏的“this”参数,并且该方法“非常”静态。
所以我的问题是在结构上调用方法时会发生什么?它与这方面的类相似吗?
是。传递的内容是包含结构的变量的别名,而不是对实例的引用。这就是struct方法可以改变结构的方式。 (当然,改变结构是一种不好的做法,但有时是必要的。)
struct S
{
int y;
public void M(int x)
{
Console.WriteLine(x + y);
}
}
...
S s = new S();
s.M(10);
在逻辑上与
相同struct S
{
int y;
public static void M(ref S _this, int x)
{
Console.WriteLine(x + _this.y);
}
}
...
S s = new S();
S.M(ref s, 10);
然后出现一个有趣的问题:如果“接收器”不是变量怎么办? (*)您只能将ref作为变量。假设你有:
GetAnS().M(10);
???那么会发生什么?
我们为你制作一个变量。那就变成了
S temporary = GetAnS();
temporary.M(10);
现在接收器是一个变量,所以我们可以将“隐藏此参数”作为别名。
(*)这里还有其他有趣的案例,比如结构是可变的但变量是只读的,等等。
答案 2 :(得分:1)
我前段时间写了一系列关于这个主题的博客文章。特别是http://www.simple-talk.com/community/blogs/simonc/archive/2010/11/02/95489.aspx
在IL中,您可以在堆栈上存储托管指针,这类似于对象引用,但它可以指向除对象引用之外的其他内容。有一些特定的指令可以获取指向某个东西的托管指针,例如ldloca
,它获取指向局部变量的指针,或ldelema
,它获取指向数组中特定元素的指针。
这些是安全指针,因为垃圾收集器知道它们,因此如果GC指向的对象被GC移动,则更改它们。
对结构的方法调用需要一个指向结构的托管指针(可以在内存中的任何位置 - 在堆栈上,在参数列表中,在堆上的另一个对象中)该方法将作为{{1执行指针。对引用类型的方法调用需要对象引用。
在调用泛型类型的方法时会出现一些问题,这些类型可以是值类型或引用类型。我稍后会在博文中看一下。
能够使用托管指针到值类型的副作用之一是IL可以在盒装值类型上调用方法而无需将其取消包装:
this