关于c#:你何时使用“this”关键字?

关于c#:你何时使用“this”关键字?

When do you use the “this” keyword?

我很好奇其他人是如何使用这个关键字的。我倾向于在构造函数中使用它,但也可以在整个类的其他方法中使用它。一些例子:

在构造函数中:

1
2
3
4
public Light(Vector v)
{
    this.dir = new Vector(v);
}

别处

1
2
3
4
5
public void SomeMethod()
{
    Vector vec = new Vector();
    double d = (vec * vec) - (this.radius * this.radius);
}

我不是说这听起来很刺耳,但没关系。

说真的。

看看那些重要的事情:你的项目,你的代码,你的工作,你的个人生活。他们中的任何一个都不会成功取决于您是否使用"this"关键字来限定对字段的访问。这个关键字不能帮助您按时发货。它不会减少错误,也不会对代码质量或可维护性产生任何明显的影响。这不会给你加薪,也不会让你在办公室的时间减少。

这真的只是一个风格问题。如果你喜欢"这个",那就用它。如果你不这样做,那么就不要这样做。如果你需要它来获得正确的语义,那么就使用它。事实上,每个程序员都有自己独特的编程风格。这种风格反映了特定程序员对于"最美观的代码"应该是什么样子的概念。根据定义,任何其他读取代码的程序员都会有不同的编程风格。这意味着总会有一些事情是你做的,而另一个人不喜欢的,或者会有不同的做法。在某个时刻,有人会读你的代码并抱怨一些事情。

我不会为此烦恼的。我只想确保代码是尽可能美观,根据您自己的口味。如果你问10个程序员如何格式化代码,你会得到大约15种不同的意见。更值得关注的是代码是如何分解的。事物抽象化了吗?我是否为事物取了有意义的名字?有很多重复的代码吗?我有办法简化这些东西吗?我认为,把这些事情做好,将对你的项目、代码、工作和生活产生最大的积极影响。巧合的是,这可能也会使另一个人抱怨最少。如果您的代码可以工作,易于阅读,并且考虑了很多因素,那么另一个人就不会仔细检查您如何初始化字段了。他只会使用你的代码,对它的伟大感到惊奇,然后继续做其他事情。


这个关键字在C中有几种用法。

  • 限定以类似名称隐藏的成员
  • 将对象本身作为参数传递给其他方法
  • 使对象从方法返回自身
  • 声明索引器
  • 声明扩展方法
  • 在构造函数之间传递参数
  • 内部重新分配值类型(结构)值。
  • 在当前实例上调用扩展方法
  • 把自己铸造成另一种类型
  • 链接在同一类中定义的构造函数
  • 您可以通过在作用域中不使用同名的成员和局部变量来避免第一次使用,例如,遵循常见的命名约定,并使用属性(pascal case)而不是字段(camel case),以避免与局部变量(camel case)发生冲突。在C 3.0中,可以通过使用自动实现的属性轻松地将字段转换为属性。


    我只在绝对必要的时候使用它,即当另一个变量正在隐藏另一个变量时。比如这里:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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;
        }

    }

    或者正如RyanFox指出的,当你需要把它作为参数传递时。(局部变量优先于成员变量)


    就我个人而言,在引用成员变量时,我总是尝试使用这个方法。它有助于澄清代码并使其更具可读性。即使没有歧义,第一次阅读我的代码的人也不知道这一点,但是如果他们看到这一点被一致使用,他们就会知道是否在查看成员变量。


    我不能相信所有人都说使用它总是一种"最佳实践"之类的。

    当有歧义时使用"this",比如在corey的例子中,或者当需要将对象作为参数传递时,比如在Ryan的例子中。没有理由使用它,否则,因为能够基于范围链解析变量应该足够清楚,不需要使用它来限定变量。

    编辑:关于"this"的C文档表明除了我提到的两个之外,还有一个用法用于"this"关键字-用于声明索引器

    编辑:@juan:huh,在我的陈述中我没有发现任何不一致的地方——有3个例子我会使用"this"关键字(如C文档中所记录的那样),而这些都是你真正需要它的时候。在没有阴影的情况下,将"this"放在构造函数中的变量前面,只不过是浪费了击键和阅读时浪费了我的时间,没有任何好处。


    我每次引用实例变量时都使用它,即使我不需要这样做。我认为这会使代码更清晰。


    只要样式警察告诉我,我就用它。必须遵守样式警察。哦,是的。


    任何时候都需要对当前对象的引用。

    一个特别方便的场景是,当您的对象正在调用一个函数并希望将自己传递给它时。

    例子:

    1
    2
    3
    4
    void onChange()
    {
        screen.draw(this);
    }

    我也倾向于在任何地方使用它,只是为了确保它是我们正在处理的实例成员。


    我在任何可能有歧义的地方使用它(很明显)。不仅编译器歧义(在这种情况下是必需的),而且对于查看代码的人来说也是歧义。


    此关键字的另一个比较少见的用法是,当需要从实现类内调用显式接口实现时。这里有一个人为的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Example : ICloneable
    {
        private void CallClone()
        {
            object clone = ((ICloneable)this).Clone();
        }

        object ICloneable.Clone()
        {
            throw new NotImplementedException();
        }
    }

    当我使用它时:

    • 从类内访问私有方法(以区分)
    • 将当前对象传递给另一个方法(或在发生事件时作为发送方对象)
    • 创建扩展方法时:d

    我不将它用于私有字段,因为我在私有字段变量名前面加了下划线(uuu)。


    [C++]

    我同意"必须使用它"的说法。不必要地用这个来修饰代码并不是一个好主意,因为编译器不会在你忘记做的时候警告你。这给人们带来了潜在的困惑,他们希望这永远存在,也就是说,他们必须考虑它。

    那么,你什么时候用呢?我刚刚查看了一些随机代码,发现了这些示例(我不会判断这些是否是好的事情):

    • 把"你自己"传递给一个函数。
    • 把"你自己"分配给一个指针或类似的东西。
    • 铸造,即上下铸造(安全或其他),铸造施工等。
    • 编译器强制消除歧义。

    当函数接受对同一类型对象的引用时,我想让它完全清楚地知道我指的是哪个对象,在哪里。

    例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class AABB
    {
      bool intersects( AABB other )
      {
        return other.left() < right() &&
               left() < other.right() &&

               // +y increases going down
               other.top() < bottom() &&
               top() < other.bottom() ;
      }
    } ;

    一瞥,right()指的是哪个AABB?this增加了一点澄清剂。


    在Jakub?图尔克关于在承包商之间传递数据的回答可能需要一些解释。这是在重载构造函数中,并且是强制使用this的一种情况。在下面的示例中,我们可以使用默认参数从无参数构造函数调用参数化构造函数。

    1
    2
    3
    4
    5
    class MyClass {
        private int _x
        public MyClass() : this(5) {}
        public MyClass(int v) { _x = v;}
    }

    我发现有时这是一个特别有用的特性。


    您应该总是使用它,我使用它来修改私有字段和参数(因为我们的命名约定声明我们不使用前缀作为成员和参数名称(它们是基于在Internet上找到的信息,所以我认为这是最佳实践)。


    我养成了在Visual C++中自由使用它的习惯,因为这样做会触发我点击">"键的智能信息,而我则懒惰。(而且容易出现打字错误)

    但我继续使用它,因为我发现调用成员函数而不是全局函数很方便。


    "this."有助于在"this"类中查找包含大量成员的成员(通常是由于深度继承链)。

    按ctrl+space并不能解决这个问题,因为它还包括类型;其中作为"this."只包括成员。

    我通常在得到我想要的东西后删除它:但这只是我的风格突破。

    在风格方面,如果你是一个独行侠,你决定;如果你为一家公司工作,坚持公司的政策(看看源代码管理中的东西,看看其他人在做什么)。就使用它来限定成员资格而言,这既不是对的也不是错的。唯一错误的是不一致性——这是风格的黄金法则。别管吹毛求疵的人。把时间花在思考真正的编码问题上——显然是编码问题上。


    我只在需要的时候使用它,除了对称操作,因为单参数多态性必须放在一边的方法中:

    1
    2
    3
    boolean sameValue (SomeNum other) {
       return this.importantValue == other.importantValue;
    }


    [C++]

    这在分配操作员中使用,大多数时间您必须检查和防止奇怪的(无意的、危险的或只是浪费程序时间)事情,例如:

    1
    2
    A a;
    a = a;

    您的分配操作员将被写入:

    1
    2
    3
    4
    5
    6
    7
    A& A::operator=(const A& a) {
        if (this == &a) return *this;

        // we know both sides of the = operator are different, do something...

        return *this;
    }

    C++编译器上的EDOCX1 0

    如果没有立即找到符号,C++编译器将默默地查找符号。有时,大多数情况下,它是好的:

    • 如果没有在子类中重载它,则使用母亲类的方法。
    • 将一个类型的值提升为另一个类型

    但有时,您只是不想让编译器猜测。您希望编译器选择正确的符号,而不是另一个。

    对于我来说,这些时间是在一个方法中,我想要访问一个成员方法或成员变量的时间。我只是不想因为我写了printf,而不是print,而随便拿起一些符号。this->printf本不会编译。

    重点是,对于C遗留库(§),几年前编写的遗留代码(§),或者在复制/粘贴是过时但仍处于活动状态的语言中可能发生的任何事情,有时告诉编译器不要玩机智是一个很好的主意。

    这就是我使用this的原因。

    (§)这对我来说仍然是一个谜,但我现在想知道,您是否在源代码中包含了头,这是所有旧的C库符号都会污染您的全局命名空间的原因。

    (§§)意识到"你需要包含一个头部,但是包含这个头部会破坏你的代码,因为它使用了一些带有通用名称的哑宏",这是一个程序员生命中的俄罗斯轮盘时刻之一。


    我倾向于在字段下加下划线,所以实际上不需要使用它。另外,R倾向于将它们重构掉…


    我几乎只在引用同一类型内部的类型属性时才使用它。正如另一个用户提到的,我还强调了本地字段,因此它们是显而易见的,不需要这样做。


    C++中没有提到过的一个用法,那不是引用自己的对象,也不是从接收变量中消除成员的歧义。

    可以使用this将非依赖名称转换为从其他模板继承的模板类内的参数依赖名称。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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
       }
    }

    模板是用一个双传递机制编译的。在第一个过程中,只解析和检查非参数依赖名称,而只检查依赖名称的一致性,而不实际替换模板参数。

    在这一步中,在不实际替换类型的情况下,编译器几乎没有关于base可能是什么的信息(请注意,基础模板的专门化可以将其转换为完全不同的类型,甚至是未定义的类型),因此它只是假设它是一个类型。在这个阶段,对于程序员来说,似乎很自然的非依赖调用f是一个符号,编译器必须将其作为derived的成员或在封闭的名称空间中找到(在示例中没有这种情况),并且它会抱怨。

    解决方案是将非依赖名称f转换为依赖名称。这可以通过两种方式来实现,一种是显式地说明实现它的类型(base::f——添加base,使符号依赖于T,编译器只会假设它存在,并在参数替换之后推迟第二遍的实际检查。

    第二种方法是,如果您继承的模板有多个参数或长名称,那么在符号之前只添加一个this->。因为您正在实现的模板类确实依赖于一个参数(它继承自base),所以this->依赖于参数,我们得到的结果相同:在模板参数替换之后,在第二轮中检查this->f


    当许多开发人员在同一代码库上工作时,您需要一些代码准则/规则。在我工作的地方,我们打算在字段、属性和事件上使用"this"。

    对我来说,这样做很有意义,当您区分类变量和方法变量时,它使代码更容易阅读。


    我每次都用它。我相信它使代码更可读,而更可读的代码等于更少的错误和更多的可维护性。


    1种常见的Java设置程序:

    1
    2
    3
     public void setFoo(int foo) {
         this.foo = foo;
     }

    2-当使用此对象作为参数调用函数时

    1
    notifier.addListener(this);

    我使用它来像johnmcg一样调用intellisense,但当我完成后,我会返回并删除"this->"。我遵循微软的惯例,在成员变量前加上"m_uux",因此将其作为文档保留是多余的。


    这取决于我使用的编码标准。如果我们使用u来表示一个实例变量,那么"this"就变得多余了。如果我们不使用u,那么我倾向于使用它来表示实例变量。


    你不应该使用"这个",除非你绝对必须这样做。

    有一种惩罚与不必要的冗长有关。您应该争取代码的长度与它需要的长度完全相同,并且不再如此。


    从未。曾经。如果您有变量阴影,那么您的命名约定就在crack上。我的意思是,真的,没有区别的成员变量命名?面罩


    推荐阅读