Когда вы используете ключевое слово "это"?

Мне было интересно, как другие люди используют это ключевое слово. Я склонен использовать его в конструкторах, но я также могу использовать его во всем классе в других методах. Некоторые примеры:

В конструкторе:

public Light(Vector v)
{
    this.dir = new Vector(v);
}

В другом месте

public void SomeMethod()
{
    Vector vec = new Vector();
    double d = (vec * vec) - (this.radius * this.radius);
}

31 ответ

Решение

Есть несколько вариантов использования этого ключевого слова в C#.

  1. Чтобы квалифицировать участников, скрытых с похожим именем
  2. Чтобы объект передавал себя в качестве параметра другим методам
  3. Чтобы объект возвращал себя из метода
  4. Объявить индексаторы
  5. Объявить методы расширения
  6. Передавать параметры между конструкторами
  7. Внутренне переназначить значение типа (структуры) значения.
  8. Чтобы вызвать метод расширения в текущем экземпляре
  9. Привести себя к другому типу
  10. Для цепочки конструкторов, определенных в одном классе

Вы можете избежать первого использования, не используя член и локальные переменные с одинаковыми именами в области видимости, например, следуя общим соглашениям об именах и используя свойства (случай Паскаля) вместо полей (случай верблюда), чтобы избежать столкновения с локальными переменными (также верблюдом) дело). В C# 3.0 поля можно легко преобразовать в свойства с помощью автоматически реализуемых свойств.

Я не хочу сказать, что это звучит странно, но это не имеет значения.

Шутки в сторону.

Посмотрите на важные вещи: ваш проект, ваш код, ваша работа, ваша личная жизнь. Ни один из них не будет зависеть от того, используете ли вы ключевое слово "this" для определения доступа к полям. Это ключевое слово не поможет вам доставить вовремя. Это не уменьшит количество ошибок, не окажет заметного влияния на качество кода и удобство сопровождения. Это не даст вам повышение или позволит вам проводить меньше времени в офисе.

Это действительно просто вопрос стиля. Если вам нравится "это", то используйте его. Если нет, то не надо. Если вам нужно получить правильную семантику, используйте ее. Правда в том, что у каждого программиста свой собственный уникальный стиль программирования. Этот стиль отражает представления конкретного программиста о том, как должен выглядеть "наиболее эстетичный код". По определению, любой другой программист, который читает ваш код, будет иметь другой стиль программирования. Это означает, что всегда будет что-то, что вы сделали, что другой парень не любит или сделал бы по-другому. В какой-то момент какой-то парень собирается прочитать ваш код и ворчать о чем-то.

Я бы не стал беспокоиться об этом. Я просто хотел бы убедиться, что код настолько эстетичен, насколько это возможно, в соответствии с вашими вкусами. Если вы спросите 10 программистов, как форматировать код, вы получите около 15 разных мнений. Лучше сосредоточиться на том, как код учитывается. Являются ли вещи абстрагированы правильно? Я выбрал значимые имена для вещей? Много ли дублирования кода? Есть ли способы, которыми я могу упростить вещи? Я думаю, что правильное решение этих вопросов окажет наибольшее положительное влияние на ваш проект, ваш код, вашу работу и вашу жизнь. По совпадению, это, вероятно, также заставит другого парня ворчать меньше всего. Если ваш код работает, его легко прочитать и он хорошо продуман, другой не будет внимательно изучать, как вы инициализируете поля. Он просто собирается использовать ваш код, восхититься его величием, а затем перейти к чему-то другому.

Я использую его только тогда, когда это абсолютно необходимо, т. Е. Когда другая переменная скрывает другую. Такие как здесь:

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;
    }

}

Или, как указывает Райан Фокс, когда вам нужно передать это как параметр. (Локальные переменные имеют приоритет над переменными-членами)

Лично я стараюсь всегда использовать это при обращении к переменным-членам. Это помогает уточнить код и сделать его более читабельным. Даже если нет никакой двусмысленности, кто-то, читающий мой код впервые, не знает этого, но если они увидят, что это используется последовательно, они будут знать, смотрят ли они на переменную-член или нет.

Я использую его каждый раз, когда обращаюсь к переменной экземпляра, даже если мне это не нужно. Я думаю, что это делает код более понятным.

Я не могу поверить, что все люди, которые говорят, что его использование всегда является "лучшей практикой" и тому подобное.

Используйте "this", когда есть двусмысленность, как в примере Кори или когда вам нужно передать объект в качестве параметра, как в примере Райана. Нет никаких оснований использовать его иначе, потому что возможность разрешать переменную на основе цепочки областей действия должна быть достаточно ясной, чтобы в ней не требовалось уточнять переменные.

РЕДАКТИРОВАТЬ: документация C# по "this" указывает на еще одно использование, помимо двух, которые я упомянул, для ключевого слова "this" - для объявления индексаторов

РЕДАКТИРОВАТЬ: @Juan: Да, я не вижу никаких противоречий в своих высказываниях - есть 3 случая, когда я использовал бы ключевое слово "this" (как описано в документации по C#), и это те моменты, когда это действительно необходимо. Помещать "это" перед переменными в конструкторе, когда не происходит теневое копирование, - это просто трата нажатий клавиш и трата моего времени на чтение, это не дает никакой пользы.

Я использую его всякий раз, когда мне подсказывает StyleCop. StyleCop должен соблюдаться. О да.

В любое время вам нужна ссылка на текущий объект.

Один особенно удобный сценарий - когда ваш объект вызывает функцию и хочет передать себя в нее.

Пример:

void onChange()
{
    screen.draw(this);
}

Я склонен использовать его повсюду, просто для того, чтобы было ясно, что мы имеем дело с членами экземпляра.

Другое несколько редкое использование для этого ключевого слова - когда вам нужно вызвать явную реализацию интерфейса из реализующего класса. Вот надуманный пример:

class Example : ICloneable
{
    private void CallClone()
    {
        object clone = ((ICloneable)this).Clone();
    }

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

Я использую его везде, где может быть неоднозначность (очевидно). Не только неоднозначность компилятора (в этом случае это потребуется), но и неоднозначность для тех, кто смотрит на код.

[C++]

Я согласен с бригадой "используй, когда надо". Излишнее оформление кода с помощью этого не является хорошей идеей, потому что компилятор не предупредит вас, когда вы забудете это сделать. Это вводит в заблуждение людей, ожидающих, что это будет всегда, то есть им придется подумать об этом.

Итак, когда вы будете использовать это? Я только что осмотрел какой-то случайный код и нашел эти примеры (я не делаю суждения о том, стоит ли это делать или нет):

  • Передача "себя" в функцию.
  • Назначение "себя" указателю или что-то в этом роде.
  • Литье, то есть литье вверх / вниз (безопасное или иное), постоянство отливки и т. Д.
  • Компилятор принудительно устраняет неоднозначность.

Вот когда я использую это:

  • Доступ к закрытым методам из класса (для разграничения)
  • Передача текущего объекта другому методу (или как объект отправителя, в случае события)
  • При создании методов расширения:D

Я не использую это для приватных полей, потому что я ставлю префикс имен приватных полей с подчеркиванием (_).

В ответе Якуба Штурца № 5 о передаче данных между конструкторами, вероятно, можно было бы использовать небольшое объяснение. Это в перегрузке конструкторов и это единственный случай, когда использование this является обязательным. В следующем примере мы можем вызвать параметризованный конструктор из конструктора без параметров с параметром по умолчанию.

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

Я обнаружил, что это особенно полезная функция в некоторых случаях.

Вы должны всегда использовать это, я использую это, чтобы дифференцировать частные поля и параметры (потому что наши соглашения об именах заявляют, что мы не используем префиксы для имен членов и параметров (и они основаны на информации, найденной в Интернете, поэтому я считаю, что лучшая практика))

Я использую его, когда в функции, которая принимает ссылку на объект того же типа, я хочу четко указать, на какой объект я ссылаюсь, где.

Например

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() ;
  }
} ;

(Против)

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 добавляет немного осветлителя.

У меня есть привычка свободно использовать его в Visual C++, так как это вызывает срабатывание IntelliSense, когда я нажимаю клавишу ">", и я ленив. (и склонны к опечаткам)

Но я продолжал использовать его, так как мне было удобно видеть, что я вызываю функцию-член, а не глобальную функцию.

[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" с большим количеством членов (обычно из-за глубокой цепочки наследования).

Нажатие CTRL+ Пробел не помогает в этом, потому что он также включает типы; где-как "это" включает членов ТОЛЬКО.

Я обычно удаляю его, как только получаю то, что мне было нужно, но это только мой стиль.

С точки зрения стиля, если вы одинокий рейнджер - вы решаете; если вы работаете в компании, придерживайтесь политики компании (посмотрите на материалы в системе контроля версий и посмотрите, что делают другие люди). С точки зрения использования его для квалификации членов, ни правильно, ни неправильно. Единственная неправильная вещь - непоследовательность - это золотое правило стиля. Оставь придирки другим. Тратьте свое время на обдумывание реальных проблем кодирования - и, очевидно, кодирования - вместо этого.

this на компиляторе C++

Компилятор C++ будет молча искать символ, если он не найдет его немедленно. Иногда, в большинстве случаев, это хорошо:

  • используя метод материнского класса, если вы не перегружали его в дочернем классе.
  • продвижение значения типа в другой тип

Но иногда вы просто не хотите, чтобы компилятор угадывал. Вы хотите, чтобы компилятор подобрал правильный символ, а не другой.

Для меня это те моменты, когда внутри метода я хочу получить доступ к методу-члену или переменной-члену. Я просто не хочу, чтобы какой-то случайный символ был поднят только потому, что я написал printf вместо print, this->printf не скомпилировал бы.

Дело в том, что с унаследованными библиотеками C (§), унаследованным кодом, написанным много лет назад (§§), или с тем, что может произойти на языке, где копирование / вставка является устаревшей, но все еще активной функцией, иногда говоря компилятору не воспроизводить остроумие это отличная идея.

Вот причины, которые я использую this,

(§) это все еще для меня загадка, но теперь мне интересно, является ли тот факт, что вы включаете заголовок в свой источник, причиной того, что все устаревшие символы библиотек C будут загрязнять ваше глобальное пространство имен

(§§) Понимание того, что "вам нужно включить заголовок, но включение этого заголовка нарушит ваш код, потому что он использует какой-то тупой макрос с общим именем", - это один из тех моментов русской рулетки в жизни кодера

Я в значительной степени использую это только при обращении к свойству типа внутри того же типа. Как упомянул другой пользователь, я также подчеркиваю локальные поля, чтобы они были заметны, не нуждаясь в этом.

Я склонен подчеркивать поля с помощью _, так что на самом деле не нужно это использовать. Также R# имеет тенденцию рефакторинг их в любом случае...

Я использую это каждый раз, когда могу. Я считаю, что это делает код более читабельным, а более читаемый код - меньше ошибок и удобнее в обслуживании.

Когда вы работаете над одной и той же кодовой базой, вы нуждаетесь в некоторых руководящих принципах / правилах. Где я работаю, мы решили использовать это в полях, свойствах и событиях.

Для меня имеет смысл сделать это следующим образом: он облегчает чтение кода, когда вы различаете переменные класса и переменные метода.

Я использую его только тогда, когда это необходимо, за исключением симметричных операций, которые из-за полиморфизма одного аргумента должны быть помещены в методы одной стороны:

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

Это зависит от стандарта кодирования, под которым я работаю. Если мы используем _ для обозначения переменной экземпляра, тогда "this" становится избыточным. Если мы не используем _, то я склонен использовать это для обозначения переменной экземпляра.

Есть одно использование, которое еще не было упомянуто в C++, и которое не должно ссылаться на собственный объект или устранять неоднозначность члена из полученной переменной.

Ты можешь использовать this преобразовать независимое имя в зависимое от аргумента имя внутри шаблонных классов, которые наследуются от других шаблонов.

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<T> может быть (обратите внимание, что специализация базового шаблона может превратить его в совершенно разные типы, даже неопределенные типы), поэтому он просто предполагает, что это тип. На этом этапе независимый вызов f это кажется естественным для программиста - это символ, который компилятор должен найти в качестве члена derived или во вложенных пространствах имен - чего не происходит в примере - и он будет жаловаться.

Решение превращает независимое имя f в зависимое имя. Это можно сделать несколькими способами, явно указав тип, в котором он реализован (base<T>::f - добавление base<T> делает символ зависимым от T и компилятор просто предположит, что он будет существовать, и отменит фактическую проверку для второго прохода после подстановки аргумента.

Второй способ, намного более сортирующий, если вы наследуете от шаблонов, имеющих более одного аргумента или длинных имен, это просто добавление this-> перед символом. Поскольку класс шаблона, который вы реализуете, зависит от аргумента (он наследуется от base<T>) this-> зависит от аргумента, и мы получаем тот же результат: this->f проверяется во втором раунде, после подстановки параметров шаблона.

Я использую его для вызова Intellisense точно так же, как JohnMcG, но я вернусь и удалю "this->", когда закончу. Я следую соглашению Microsoft с префиксом переменных-членов с помощью "m_", поэтому оставить это как документацию просто излишне.

1 - идиома общего Java-сеттера:

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

2 - при вызове функции с этим объектом в качестве параметра

notifier.addListener(this);

Вы не должны использовать "это", если вы абсолютно не должны.

Существует наказание, связанное с ненужным многословием. Вы должны стремиться к коду, который ровно столько, сколько нужно, и больше.

Другие вопросы по тегам