Daniel Arant
Daniel Arant

Reputation: 494

Does the C# compiler hoist variable declarations out of methods called within loops?

I have a method that calls a helper method from within a for loop. The helper method contains a relatively expensive variable declaration and definition that involves reflection (see below.) I'm wondering if the compiler can be counted on to inline the method call and hoist the declaration and definition out of the loop, or if I need to refactor the method in order to guarantee that the definition statement isn't executed with each iteration.

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

Update:

Here is the alternative I'm envisioning, for clarification purposes:

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

Upvotes: 0

Views: 1081

Answers (2)

Ben Voigt
Ben Voigt

Reputation: 283783

There are no expensive variable declarations in C#. In fact, MSIL doesn't even distinguish between variables declared inside a loop vs outside, they are all just local variables of the method (naturally there is a distinction between variables in the looping method and variables in the helper method).

Initialization might be expensive, but moving it outside the loop would significantly change the behavior of your code.

Upvotes: 1

Lucas Trzesniewski
Lucas Trzesniewski

Reputation: 51410

The compiler won't do such thing, because it wouldn't be an optimization, but it would change the meaning of your code. Calling a function once is not the same thing as calling it multiple times, and the compiler is only allowed changes that don't alter the effects of the program when observed from the same thread. It cannot detect whether the function is pure or not.

As a side note, the compiler won't even inline function calls. It's the job of the JIT.

Upvotes: 1

Related Questions