Поднимает ли компилятор C# объявления переменных из методов, вызываемых внутри циклов?
У меня есть метод, который вызывает вспомогательный метод изнутри for
петля. Вспомогательный метод содержит относительно дорогое объявление и определение переменной, которое включает отражение (см. Ниже). Мне интересно, можно ли рассчитывать на компилятор, чтобы встроить вызов метода и вывести объявление и определение из цикла, или если мне нужно провести рефакторинг метода, чтобы гарантировать, что оператор определения не выполняется с каждой итерацией.
private class1[] BuildClass1ArrayFromTestData()
{
var class1Count = int.Parse(testContextInstance.DataRow["class1[]"].ToString());
var class1s = new List<class1>(class1Count);
for (var c = 0; c < class1Count; c++)
{
class1s.Add(BuildClass1FromTestData(string.Format("class1[{0}]", c)));
}
return class1s.ToArray();
}
private class1 BuildClass1FromTestData(string testContextName)
{
DataColumnCollection columns = testContextInstance.DataRow.Table.Columns;
var class1Fields = typeof(class1).GetFields();
var class1Object = new class1();
foreach (var field in class1Fields)
{
var objectContextName = string.Format("{0}.{1}", testContextName, field.Name);
if (!columns.Contains(objectContextName))
continue;
// Assume that all fields are of type "string" for simplicity
field.SetValue(
class1Object,
testContextInstance.DataRow[objectContextName].ToString()
);
}
return class1Object;
}
Обновить:
Вот альтернатива, которую я представляю, для пояснения:
private class1[] BuildClass1ArrayFromTestData()
{
var class1Count = int.Parse(testContextInstance.DataRow["class1[]"].ToString());
var class1s = new List<class1>(class1Count);
// Moved from BuildClass1FromTestData()
DataColumnCollection columns = testContextInstance.DataRow.Table.Columns;
var class1Fields = typeof(class1).GetFields();
for (var c = 0; c < class1Count; c++)
{
class1s.Add(BuildClass1FromTestData(string.Format("class1[{0}]", c)), columns, class1Fields);
}
return class1s.ToArray();
}
private class1 BuildClass1FromTestData(string testContextName, DataColumnCollection columns, FieldInfo[] class1Fields)
{
var class1Object = new class1();
foreach (var field in class1Fields)
{
var objectContextName = string.Format("{0}.{1}", testContextName, field.Name);
if (!columns.Contains(objectContextName))
continue;
// Assume that all fields are of type "string" for simplicity
field.SetValue(
class1Object,
testContextInstance.DataRow[objectContextName].ToString()
);
}
return class1Object;
}
2 ответа
В C# нет дорогих объявлений переменных. На самом деле, MSIL даже не различает переменные, объявленные внутри цикла, против внешних, все они являются просто локальными переменными метода (естественно, существует различие между переменными в методе циклического преобразования и переменными в вспомогательном методе).
Инициализация может быть дорогой, но перемещение ее за пределы цикла значительно изменит поведение вашего кода.
Компилятор не будет делать этого, потому что это не будет оптимизацией, но это изменит смысл вашего кода. Вызов функции один раз - это не то же самое, что вызов ее несколько раз, и компилятору разрешены только те изменения, которые не влияют на результаты программы при наблюдении из одного и того же потока. Он не может определить, является ли функция чистой или нет.
Как примечание, компилятор не будет даже встроенных вызовов функций. Это работа JIT.