你什么时候使用“this”关键字?
我很好奇其他人如何使用这个关键字。 我倾向于在构造函数中使用它,但是我也可以在其他方法中在整个类中使用它。 一些例子:
在构造函数中:
public Light(Vector v) { this.dir = new Vector(v); }
别处
public void SomeMethod() { Vector vec = new Vector(); double d = (vec * vec) - (this.radius * this.radius); }
在C#中有几个这个关键字的用法。
- 使具有相似名称的成员具有资格
- 有一个对象作为parameter passing给其他方法
- 让一个对象从一个方法返回
- 宣布索引器
- 声明扩展方法
- 在构造函数之间传递参数
- 要内部重新分配值types(结构)值 。
- 调用当前实例的扩展方法
- 把自己转换成另一种types
- 链接在同一个类中定义的构造函数
你可以通过在范围内不使用成员variables和局部variables来避免第一次使用,例如通过遵循常见的命名约定和使用属性(Pascal case)而不是字段(camel case)来避免与局部variables(也是骆驼案件)。 在C#3.0中,可以使用自动实现的属性轻松地将字段转换为属性 。
我并不是说这听起来很尖锐,但是没关系。
认真。
看看重要的事情:你的项目,你的代码,你的工作,你的个人生活。 他们都不会取决于您是否使用“this”关键字来限定对字段的访问权限。 这个关键字不会帮你按时发货。 这不会减less错误,对代码质量或可维护性没有任何明显的影响。 这不会让你加薪,或者让你在办公室花更less的时间。
这真的只是一个风格问题。 如果你喜欢“这个”,然后使用它。 如果你不这样做,那就不要。 如果你需要它来得到正确的语义,然后使用它。 事实是,每个程序员都有自己独特的编程风格。 这种风格反映了特定的程序员关于“最美观的代码”应该是什么样子的概念。 根据定义,读取你的代码的任何其他程序员将会有不同的编程风格。 这意味着总会有一些你不喜欢的东西,或者做得不一样。 在某个时候,有些人会阅读你的代码,并为此发牢骚。
我不会为此烦恼的。 我只是要确保代码是根据自己的口味尽可能美观。 如果你问10位程序员如何格式化代码,你将会得到大约15种不同的意见。 一个更好的重点是代码如何被分解。 事情是抽象的吗? 我有没有select有意义的名字? 是否有很多代码重复? 有什么方法可以简化东西? 对于你的项目,你的代码,你的工作和你的生活,我认为正确的做法是最好的。 巧合的是,这可能也会导致另一个人至less抱怨。 如果你的代码工作,很容易阅读,而且是很好的考虑,另一个人不会仔细检查你如何初始化字段。 他只是要使用你的代码,惊叹于它的伟大,然后转向别的东西。
我只在绝对必要的时候才使用它,也就是说,当另一个variables遮蔽另一个variables时。 如在这里:
class Vector3 { float x; float y; float z; public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } }
或者像Ryan Fox指出的那样,当你需要把这个作为parameter passing的时候。
就个人而言,我试图在引用成员variables时总是使用这个 。 它有助于澄清代码并使其更具可读性。 即使没有歧义,第一次通读我的代码的人也不知道,但如果他们看到这个一致的使用,他们会知道他们是否正在查看一个成员variables。
我不能相信所有说使用它的人都是“最佳实践”等等。
当存在歧义时,使用“this”,就像在Corey的例子中,或者当你需要传递这个对象作为参数时,就像Ryan的例子 。 没有理由使用它,否则因为能够解决基于范围链的variables应该是足够清楚的,使用它的限定variables应该是不必要的。
编辑:关于“这个”的C#文档表示除了我提到的两个“this”关键字 – 用于声明索引器
编辑:@Juan:嗯,我没有看到任何不一致的声明 – 有3个实例,当我使用“这个”关键字(如C#文档中所述),这些是你真正需要它的时候。 在没有阴影的情况下,在构造函数的variables前面粘贴“this”只是浪费了按键,浪费了我的时间,所以没有任何好处。
我每次使用它都会引用一个实例variables,即使我不需要。 我认为这会使代码更清晰。
每当StyleCop告诉我时,我都会使用它。 StyleCop必须服从。 哦,是的。
任何时候你需要一个对当前对象的引用。
一个特别方便的场景是当你的对象正在调用一个函数,并且想把它传递给它。
例:
void onChange() { screen.draw(this); }
我也倾向于在任何地方使用它,只是为了确保我们正在处理的是实例成员。
我在任何可能有歧义的地方使用它(显然)。 不仅仅是编译模糊(在这种情况下是需要的),而且对于查看代码的人来说也是不明确的。
这个关键字的另一个less见的用法是当你需要从实现类中调用一个明确的接口实现时。 这是一个人为的例子:
class Example : ICloneable { private void CallClone() { object clone = ((ICloneable)this).Clone(); } object ICloneable.Clone() { throw new NotImplementedException(); } }
这是当我使用它:
- 在课堂上访问私有方法(区分)
- 将当前对象传递给另一个方法(或作为发送者对象,如果发生事件)
- 创build扩展方法时:D
我不使用私有字段,因为我用专用字段variables名称加下划线(_)。
[C ++]
我同意“使用它时你必须”旅。 用这种方法不必要地修改代码并不是一个好主意,因为编译器在忘记执行时不会提醒你。 这引起了人们期望这种情况永远在那里的潜在困惑,也就是说,他们必须考虑这个问题。
那么,你什么时候使用它? 我只是看了一些随机代码,发现了这些例子(我不是在判断这些东西是好还是不好 )。
- 将“自己”传递给一个函数。
- 指定“你自己”的指针或类似的东西。
- 铸造,即上/下铸造(安全或其他),铸造坚固等
- 编译器强制消除歧义。
我使用它的时候,在一个接受对同一types对象的引用的函数中,我想使它完全清楚我指的是哪个对象,在哪里。
例如
class AABB { // ... members bool intersects( AABB other ) { return other.left() < this->right() && this->left() < other.right() && // +y increases going down other.top() < this->bottom() && this->top() < other.bottom() ; } } ;
(VS)
class AABB { bool intersects( AABB other ) { return other.left() < right() && left() < other.right() && // +y increases going down other.top() < bottom() && top() < other.bottom() ; } } ;
AABB一眼就能看出right()
是指什么? this
增加了一点澄清。
你应该总是使用它,我用它来区分私人领域和参数(因为我们的命名约定声明,我们不使用成员和参数名称的前缀(他们是基于在互联网上find的信息,所以我认为,最佳实践))
我习惯于在Visual C ++中宽泛地使用它,因为这样做会触发智能感知,我点击'>'键,而且我很懒。 (并容易发生错别字)
但我继续使用它,因为我发现它很方便地看到我正在调用成员函数而不是全局函数。
在JakubŠturc的回答中,关于在构造器之间传递数据的#5可能会用一点解释。 这是在重载构造函数,是一个使用this
是强制性的情况。 在以下示例中,我们可以使用默认参数从无参数构造函数中调用参数化构造函数。
class MyClass { private int _x public MyClass() : this(5) {} public MyClass(int v) { _x = v;} }
我偶然发现这是一个特别有用的function。
我倾向于用_强调字段,所以不需要使用这个。 另外R#往往重构他们呢?
我几乎只在相同types的引用types属性时才使用它 。 正如另一位用户提到的,我也强调本地领域,所以他们是显而易见的,而不需要这个 。
我只有在需要的时候才使用它,除了由于单参数多态性引起的对称操作必须被放入一方的方法:
boolean sameValue (SomeNum other) { return this.importantValue == other.importantValue; }
[C ++]
这是在赋值运算符中使用的,大多数时候你必须检查和防止奇怪的(无意,危险,或只是浪费时间的程序)的东西,如:
A a; a = a;
您的作业操作员将被写入:
A& A::operator=(const A& a) { if (this == &a) return *this; // we know both sides of the = operator are different, do something... return *this; }
this
在一个C ++编译器上
如果C ++编译器没有立即find它,它将静静地查找符号。 有时候,大多数时候,这是很好的:
- 使用母类的方法,如果你没有在子类中重载它。
- 将一种types的价值提升为另一种types
但有时候, 你只是不希望编译器猜测。 你希望编译器拿起正确的符号,而不是另一个。
对我来说 ,那些时间是在一个方法内,我想访问一个成员方法或成员variables。 我只是不想要一些随机符号,因为我写了printf
而不是print
。 this->printf
不会编译。
关键是,C语言库(§),多年前写的遗留代码(§§),或者复制/粘贴过时但仍然活跃的语言中可能发生的任何事情,有时会告诉编译器不要播放智慧是一个好主意。
这是我使用this
的原因。
(§)对我来说还是一个谜,但是我现在想知道在源代码中包含<windows.h>头文件的原因是所有传统的C库符号都会污染你的全局名称空间
(§§)认识到“你需要包含一个头文件,但是包含这个头文件将会破坏你的代码,因为它使用了一个通用名称的愚蠢的macros”是编码者生活中的俄罗斯轮盘时刻之一
我用它像JohnMcG一样调用Intellisense ,但是当我完成的时候,我会回去清除“this->”。 我遵循微软公约的前缀成员variables与“m_”,所以把它作为文件将是多余的。
'这个。' 有很多成员(通常是由于深度inheritance链)帮助find“这个”类的成员。
点击CTRL +空格对此没有帮助,因为它也包含types; 这就是“这个”。 仅包括会员。
一旦我有了以后我通常会删除它,但这只是我的风格突破。
在风格方面,如果你是一个单独的游侠 – 你决定; 如果你为一家公司工作,坚持公司的政策(看看源代码控制的东西,看看别人在做什么)。 就使用它来限定成员而言,既不是对也是错。 唯一错误的是不一致 – 这是风格的金科玉律。 不要挑剔别人。 花点时间思考真正的编码问题 – 而不是编码 – 而是。
这取决于我正在使用的编码标准。 如果我们用_来表示一个实例variables,那么“this”就变成了冗余。 如果我们不使用_那么我倾向于用这个来表示实例variables。
@dicroce:“有不必要的冗长相关的惩罚” – 什么样的惩罚? 当然不是一个性能损失..也许源文件将占用硬盘上更大的空间量? 还是whaaat?
1 – 通用的Java setter习惯用法:
public void setFoo(int foo) { this.foo = foo; }
2 – 以此对象作为参数调用函数时
notifier.addListener(this);
我每次都可以使用它。 我相信它使得代码更具可读性,更可读的代码就意味着更less的错误和更多的可维护性。
当许多开发人员使用相同的代码库时,您需要一些代码准则/规则。 在我工作的地方,我们希望在字段,属性和事件上使用“this”。
对我来说这样做是很有意义的,当区分类variables和方法variables时,它使代码更容易阅读。
有一种用法在C ++中还没有被提及,那就是不引用自己的对象或者从接收到的variables中消除歧义。
您可以使用它将非依赖名称转换为从其他模板inheritance的模板类中的依赖于参数的名称。
template <typename T> struct base { void f() {} }; template <typename T> struct derived : public base<T> { void test() { //f(); // [1] error base<T>::f(); // quite verbose if there is more than one argument, but valid this->f(); // f is now an argument dependent symbol } }
模板使用双通道机制进行编译。 在第一遍中,只parsing和检查非参数相关名称,而仅从相关性检查相关名称,而不实际replace模板参数。
在那个步骤中,如果没有真正replacetypes,编译器几乎没有关于什么base<T>
信息(注意,基本模板的专门化可以把它变成完全不同的types,甚至是未定义的types),所以它只是假定这是一种types。 在这个阶段,对程序员来说看起来很自然的非依赖调用f
是编译器必须find的作为derived
成员或包含名称空间的一个符号 – 这在示例中不会发生 – 它会抱怨。
解决方法是将非依赖名称f
转换为依赖名称。 这可以通过几种方式来完成,通过明确指定实现的types( base<T>::f
添加base<T>
使得符号依赖于T
,编译器会假定它会在参数replace之后存在并推迟第二遍的实际检查。
第二种方法,如果从具有多个参数或长名称的模板inheritance,则只需在符号前添加this->
。 因为你正在实现的模板类依赖于一个参数(它inheritance自base<T>
) this->
是依赖于参数的,所以我们得到相同的结果: this->f
在第二轮中被检查,在模板参数replace之后。