Согласно спецификации это ожидаемое поведение.
Поведение первого определяется разделом 7.3 спецификации:
Операнды в выражении оцениваются слева направо. Например, в F(i) + G(i++) * H(i)
метод F вызывается с использованием старого значения i, затем метод G вызывается со старым значением i, и, наконец, метод H вызывается с новым значением i. Это отдельно от приоритета оператора и не связано с ним.
Таким образом, в x==x++
сначала вычисляется левый операнд (0
), затем вычисляется правая рука (0
, x
становится 1
), затем выполняется сравнение: 0 == 0
верно.
Поведение второго определяется разделом 7.5.5:
- Если M является членом функции экземпляра, объявленным в типе значения :
- E оценивается. Если эта оценка вызывает исключение, дальнейшие шаги не выполняются.
- Если E не классифицируется как переменная, то создается временная локальная переменная типа E, и этой переменной присваивается значение E. Затем E переклассифицируется как ссылка на эту временную локальную переменную. Временная переменная доступна как это в пределах M, но никаким другим способом. Таким образом, только когда E является истинной переменной, вызывающий может наблюдать изменения, которые M вносит в это.
- Список аргументов оценивается, как описано в §7.5.1.
- M вызывается. Переменная, на которую ссылается E, становится переменной, на которую ссылается this.
Обратите внимание, что типы значений передаются по ссылке в их собственные методы.
Таким образом, в x.Equals(x++)
сначала оценивается цель (E - x
, переменная), затем оцениваются аргументы (0
, x
становится 1
), затем выполняется сравнение: x.Equals(0)
ложно.
РЕДАКТИРОВАТЬ: Я также хотел отдать должное отозванный сейчас комментарий dtb, опубликованный, когда вопрос был закрыт. Я думаю, что он говорил то же самое, но с ограничением длины комментариев он не смог выразить это полностью.