9

当使用“this”关键字时,系统如何知道使用什么?

最近,我在一次采访中被问到这个问题。从来没有考虑过这一点,我回复说系统将知道控制流所在的当前上下文,并决定要使用的对象而不是这个。面试官看起来不太高兴,他继续下一个问题。

谁能告诉我面试官可能想问什么,答案是什么?(我认为这可以用不同的方式解释,因此除非有人指出不这样做,否则将其保留为 wiki。)

4

11 回答 11

13

尽管指出“this”引用本质上作为魔术“隐藏参数”传递给调用的答案基本上是正确的,但 C# 中的完整故事实际上比乍一看要复杂得多。

引用类型很简单;检查引用的对象是否为空,然后在概念上将其作为名为“this”的未命名、非变量参数传递。这个故事因值类型而变得复杂。

请记住,根据定义,值类型是按值传递的——也就是说,通过复制数据来传递。因此他们的名字。但显然可变值类型——它们是纯粹的邪恶,应该避免——不能作为“this”通过值传递,因为如果你调用了一个 mutator,mutator 方法中的“this”将改变副本,而不是原本的!

因此,在值类型的方法调用中,“this”不是接收者的,而是代表接收者存储位置的变量的别名。我们通过传递“this”作为接收者的托管地址来实现这一点,而不是接收者的

现在我们可以提出另一个困难。如果存储被变异值的变量是只读变量怎么办?现在我们该怎么办?如果您好奇,请阅读我关于该主题的文章,看看您是否能正确回答所提出的难题:

http://blogs.msdn.com/ericlippert/archive/2008/05/14/mutating-readonly-structs.aspx

于 2009-08-21T16:50:27.467 回答
12

this关键字是指向当前对象的指针。类的所有非静态成员函数都可以访问 this 指针。

指向当前对象的指针通常由编译器通过使用寄存器(通常是 ECX)在非静态成员函数中提供。因此,当您编写this非静态成员函数时,编译器会将该调用转换为从 ECX 加载地址。

检查这个简单的例子:

在;
t.Test();
004114DE lea ecx,[t]
004114E1 调用 std::operator > (41125Dh)

在调用非静态成员函数之前Test(),编译器使用 [t] 加载寄存器 ECX(变量 t 的地址 - 将this在 Test 方法中)。

004114DE lea ecx,[t]

在函数内部,它可以使用 ecx 来获取当前对象实例的地址。

于 2009-08-21T15:07:28.913 回答
9

this是对象所有方法中的隐藏参数,包含实例指针的副本。

于 2009-08-21T14:53:44.053 回答
2

考虑这个类

class A {
private:
    int data;
public:
    void SetData(int arg) {
        this->data = arg;
    }
}

以及调用 SetData() 的代码:

A objA;
objA.SetData(1);

编译上述代码时,编译器会为成员函数发出与此等效的内容:

void SetData(A* this, int arg) {
     this->data = arg;
}

并且调用代码被转换成这样的:

A objA;
SetData(&objA, 1);

这意味着在编译时:

  1. 成员函数被转换为简单的全局函数。

  2. 成员函数“所属”的类实例只是作为第一个参数传递给它们(或者更确切地说是传递它的地址)。

因此,总而言之,您在代码中称为 this 指针的内容只是作为函数的第一个参数结束。这就是“系统知道”通过“this”指针访问哪个对象的方式。

上面的例子是 C++ 的。如果您暂时忘记了 CLR 和 JITting 以及所有这些,那么 C# 中发生的事情在概念上是相同的。

于 2009-08-21T15:17:49.723 回答
1

在运行时“this”将解析为指向当前对象的指针,因此“系统”将能够调用该对象上的相应方法。

于 2009-08-21T14:54:03.960 回答
1

我会回答“它是对当前类实例的引用”。

于 2009-08-21T14:54:46.283 回答
1

编译器负责在编译时正确解析该引用。如果你想了解更多关于他们使用的一些技术的信息,这本书会告诉你你需要知道的一切:
http ://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools
龙书

于 2009-08-21T15:21:31.340 回答
0

在 c 中,this 指针是指向您的类的不可见指针参数。

所以本质上,类方法的第一个参数是指向类本身的指针。您可以使用它来引用它。

于 2009-08-21T14:54:27.190 回答
0

Shematicaly 编译器会转换这样的代码:

pObject->someMethod(A,B,C)

如果“someMethod”不是虚拟的,则进入此类代码:

someMethod(pObject,A,B,C) 

如果'someMethod'是虚拟的,或者进入这样的代码:

(*pObject->vtable[someMethodIndex]) (pObject, A,B,C)

并且在您放置“this”关键字的任何地方都使用第一个参数来代替它;

当然,编译器可以通过删除第一个参数并使用一些 CPU 寄存器(通常是 esx)来存储对象的地址来优化/简化。

于 2009-08-21T15:20:11.250 回答
0

我的回答是“谁在乎?它知道。如果我需要更详细的信息,我会用谷歌搜索。”

您显然知道使用“this”会产生什么效果,这肯定是重要的事情。对于 99% 的编程任务,我会认为如何在内部解决它的细节是琐事。

于 2009-08-21T15:23:55.487 回答
0

“this”运算符将指向当前对象。“this”运算符的存在将产生影响的示例如下:

public class MyClass
{
    int value;
    public void Test(int value)
    {
        MessageBox.Show(value); // Will show the parameter to the function
        MessageBox.Show(this.value); // Will show the field in the object
    }
}

请注意,如果“this”运算符在子类中被覆盖,则不会更改将调用的虚函数

public class MyClass
{
    public virtual void Test() {}
    public void CallTest()
    {
        this.Test();
    }
}
public class MyClass2 : MyClass
{
    public override void Test() {}
}

如果执行以下代码

MyClass c = new MyClass2();
c.CallTest();

仍将调用 MyClass2.Test() 而不是 MyClass.Test()

所以“this”操作符只是告诉你,你正在访问在类级别声明的东西。

于 2009-08-21T15:45:14.633 回答