Стиль кодирования: назначения внутри выражений?

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


Вариант ①

// How many spaces are there in the beginning of string? (and remove them)
int spaces = text.Length;
text = text.TrimStart(' ');
spaces -= text.Length;
  • Преимущество: Назначение на отдельной строке, поэтому побочный эффект явно
  • Недостаток: Первая строка выглядит бессмысленной сама по себе; вы должны заметить третью строку, чтобы понять ее

Вариант ②

// How many spaces are there in the beginning of string? (and remove them)
int spaces = text.Length - (text = text.TrimStart(' ')).Length;
  • Преимущество: Заявление имеет смысл с точки зрения вычислений, которые он выполняет.
  • Недостаток: Назначение, скрытое внутри выражения; побочный эффект можно упустить.

Ответ 1

Мне не нравится ни один из них. Некоторые рекомендации по написанию четкого кода:

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

Вариант (1) нарушает это руководство; переменные "пробелы" прокомментированы как означающие "сколько пробелов в тексте", но это никогда не имеет такого значения! Он начинает свою жизнь за счет количества символов в тексте и заканчивает свое время жизни как количество пробелов, которые раньше были в тексте. Это означает две разные вещи на протяжении всей жизни, и ни один из них не является документированным.

  • Выражение выражения имеет только один побочный эффект. ( "Оператор выражения" - это оператор, состоящий из одного выражения, а в С# выражения для операторов - вызовы методов, построения объектов, приращения, декременты и назначения).

  • Выражение не имеет побочных эффектов, кроме случаев, когда выражение является единственным побочным эффектом выражения.

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

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

string originalText = text;
string trimmedText = originalText.TrimStart(' ');
int removedSpaces = originalText.Length - trimmedText.Length;
text = trimmedText;

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

Ответ 2

Я бы сделал вариант 1b:

int initial_length = text.Length;
text = text.TrimStart(' ');
int spaces = initial_length - text.Length;

Конечно, это почти дубликат варианта один, но он немного понятнее (и вам может понадобиться начальная длина вашей строки позже).

Ответ 3

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

Ответ 4

Как насчет перегрузки?

public static string TrimStart(this string s, char c, out int numCharsTrimmed) 
{
    numCharsTrimmed = s.Length;
    s = s.TrimStart(c);
    numCharsTrimmed -= s.Length;    
}

Ответ 5

Вариант ① Весь день. Это читаемо. Вариант ② гораздо труднее поддерживать.

Ответ 6

С точки зрения вашего вопроса я бы сказал, что не выполнять назначения в выражениях, потому что он не поддерживается на всех языках, например Python, поэтому, если вы хотите оставаться последовательным в своем личном стиле кодирования, вы может придерживаться традиционных заданий.