结构上的方法调用?

时间:2011-05-05 09:28:44

标签: c# .net struct

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

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

3 个答案:

答案 0 :(得分:24)

根据CIL specthis指针通过引用/作为托管指针传递。正如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