Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Бог ты мой, вы нашли множество дополнительных ситуаций, в которых рефакторинг «устранить переменную» может работать неправильно. Вот всего несколько ваших наблюдений: (опять-таки, в каждом случае устраняется переменная «x»).
Проблема возникает каждый раз, когда x рассматривается как переменная, а не как значение. Такие очевидные случаи, когда x находится слева от оператора присваивания, ей присваивается результат оператора ++, или x используется в качестве “ref” или “out” параметра, являются весьма простыми. Однако бывают случаи, когда не столь очевидно, что x выступает в них в качестве переменной. Давайте предположим, что S – это изменяемая структура с полем F:
S x = new S();
x.F = 123;
В этом случае мы не можем изменить этот код на (new S()).F = 123, поскольку new S() не является переменной. Изменяемое поле требует наличия некоторого хранилища, которое может быть изменено, однако в этом случае у нас его нет.
Ряд людей обратили внимание на то, что правила языка C# об использовании простых имен, могут приводить к проблемам:
int y = 2;
void M()
{
Action x = () => { Console.WriteLine(y); }; // refers to this.y
{
string y = "hello";
x();
}
Оба использования простого имени y в данном случае вполне корректны, поскольку области объявлений, в которых эти имена впервые используются, не пересекаются. Однако применение нашего рефакторинга приводит к их пересечению; этот код должен быть преобразован в
void M()
{
{
string y = "hello";
((Action)(()=>{Console.WriteLine(this.y);})))();
}
И ситуация становится еще хуже, если простые имена не могут быть полностью квалифицированы:
Action x = () => {int z = 123;};
{
string z = "hello";
x();
}
В этом случае, для успешного завершения рефакторинга, одна из локальных переменных с именем z должна быть переименована.
У вас также могут возникнуть проблемы и с анонимными типами:
int x = 42;
var a = new { x };
Этот код должен быть преобразован в
var a = new { x = 42 };
Аналогично,
int x = k.y( );
var a = new { x };
не может быть преобразован в
var a = new { k.y() };
поскольку это приведет к изменению имени свойства анонимного метода.
Перемещение выражения может также привести к нарушению правил определенного присваивания (definite assignment rules); следующий код не может быть изменен с помощью нашего рефакторинга:
void Foo(out int b)
{
int x = b = R();
if (Q()) return;
doSomething(x);
}
Поскольку он будет преобразован следующим образом
void Foo(out int b)
{
if (Q()) return;
doSomething(b = R());
}
И мы получим метод, который возвращает управление без присвоения значения выходному (out) параметру.
Я уже упоминал о том, что перемещение выражения может привести к изменению семантики программы, поскольку может измениться порядок наблюдаемых побочных эффектов. Было найдено множество жутких случаев, когда методы зависят от побочных эффектов; наиболее коварным из которых является следующий:
int x = LocksFoo();
lock (Bar) { return M(x); }
Рефакторинг изменяет порядок захвата блокировок Foo и Bar, что может привести к взаимной блокировке, если другой код, зависимый от Foo, всегда захватывал блокировку до захвата блокировки Bar.
Выражения инициализаторов массивов (array initializers) корректны только в небольшом количестве ситуаций; и данный рефакторинг должен это учитывать:
int[] x = {0,1};
Console.WriteLine(x);
Данной код должен быть преобразован в
Console.WriteLine(new int[] {0, 1});
И наконец, перемещение выражений может переместить его из проверяемого (checked) контекста в непроверяемый (unchecked), и наоборот:
int zero = 0;
int one = 1;
int x = int.MaxValue + one;
Console.WriteLine(checked(x + zero));
Простая реализация данного рефакторина изменит нормально работающую программу, в программу, которая завершается неудачно во время выполнения:
Console.WriteLine(checked(int.MaxValue + one + zero));
Для того, чтобы программа оставалась корректной, рефакторинг должен вводить блок unchecked перед первым добавлением!
Всем спасибо, это было очень познавательно.