Подтвердить что ты не робот

Почему существует разница в поведении приложения, созданного в VS 2010 v.s. VS 2012?

Я проверял, не устанавливает ли установка .NET 4.5 на наших машинах сборки выходное изображение IL, сгенерированное VS 2010.

Поскольку я знаю, что поведение foreach изменилось в .NET 4.5, чтобы избежать проблем из-за Доступ к Измененному закрытию, я выбрал простой приложение, которое демонстрирует поведение.

  class Program
    {
        private static void Main(string[] args)
        {
            var contents = new List<Func<int>>();
            var s = new StringBuilder();

            int[] values = new int[] { 4, 5, 6 };

            foreach (int value in values)
            {
                contents.Add(() => value);
            }

            for (var k = 0; k < contents.Count; k++)
                s.Append(contents[k]());

            Console.WriteLine(s);
        }

Выход VS 2010: 666

Выход VS 2012: 456

Я создал консольное приложение в VS 2010 и консольное приложение с тем же кодом в VS 2012 (оба целевые .NET 4).

Тем не менее, оба консольных приложения отображали разные типы поведения на основе IDE, с которыми они были построены. В выводе сборки я проверил, что оба имеют почти похожие аргументы сборки. Поэтому мне было интересно, как конечный исполняемый файл проявил различное поведение?.NET 4.5 - это обновление на месте, поэтому компилятор для обеих IDE должен быть одним и тем же.

ПРИМЕЧАНИЕ. Я рассмотрел связанный с этим вопрос: Различные ответы LINQ в VS 2010 и VS 2012, но он не ответил на мой вопрос о том, почему поведение исполняемого файла различались.

РЕДАКТИРОВАТЬ 1: Как упоминалось mletterle, я попытался создать код с помощью командной строки в окне вывода VS 2010 в командной строке VS 2010. Полученный результат вел себя так, как если бы он был построен с VS 2012.

ИЗМЕНИТЬ 2:

Я публикую вывод, присутствующий в окне вывода:

VS 2010: Сборка началась 12/20/2012 11:04:56.

CoreClean: создание каталога "obj\x86\Debug \". GenerateTargetFrameworkMonikerAttribute: пропустить цель "GenerateTargetFrameworkMonikerAttribute", потому что все выходные файлы актуальным в отношении входных файлов. CoreCompile:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe/noconfig /nowarn: 1701,1702/nostdlib +/платформа: x86/errorreport: prompt/warn: 4 /define: DEBUG; TRACE/errorendlocation/preferreduilang: en-US /highentropyva -/reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\Microsoft.CSharp.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\mscorlib.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Core.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Data.DataSetExtensions.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Data.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\system.Xml.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Xml.Linq.dll" /debug +/debug: full/filealign: 512/optimize- /out:obj\x86\Debug\TestConsoleApp.exe/target: exe/utf8output Свойства Program.cs\AssemblyInfo.cs "C:\Users\105044960\AppData\Local\Temp.NETFramework, Version = v4.0.AssemblyAttributes.cs" _CopyAppConfigFile: пропустить цель "_CopyAppConfigFile", потому что все выходные файлы являются актуальными в отношении входных файлов. CopyFilesToOutputDirectory: копирование файла из "obj\x86\Debug\TestConsoleApp.exe" в "bin\Debug\TestConsoleApp.exe". TestConsoleApp → C:\Users\105044960\Documents\Visual Studio 2010\Projects\TestConsoleApp\TestConsoleApp\Bin\Debug\TestConsoleApp.exe Копирование файла из "obj\x86\Debug\TestConsoleApp.pdb" в "Bin\Debug\TestConsoleApp.pdb".

VS 2012:

1 > CoreClean: 1 > Удаление файла "c:\users\105044960\documents\visual студия 11\Проекты\TestConsoleApp\TestConsoleApp\Bin\Debug\TestConsoleApp.exe". 1 > Удаление файла "c:\users\105044960\documents\visual studio 11\Проекты\TestConsoleApp\TestConsoleApp\Bin\Debug\TestConsoleApp.pdb". 1 > Удаление файла "c:\users\105044960\documents\visual studio 11\Проекты\TestConsoleApp\TestConsoleApp\OBJ\Debug\TestConsoleApp.csprojResolveAssemblyReference.cache". 1 > Удаление файла "c:\users\105044960\documents\visual studio 11\Проекты\TestConsoleApp\TestConsoleApp\OBJ\Debug\TestConsoleApp.exe". 1 > Удаление файла "c:\users\105044960\documents\visual studio 11\Проекты\TestConsoleApp\TestConsoleApp\OBJ\Debug\TestConsoleApp.pdb". 1 > GenerateTargetFrameworkMonikerAttribute: 1 > Пропуск цели "GenerateTargetFrameworkMonikerAttribute", потому что все выходные файлы актуальным в отношении входных файлов. 1 > CoreCompile: 1 > C:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe/noconfig /nowarn: 1701,1702,2008/nostdlib +/платформа: AnyCPU/errorreport: prompt /warn: 4/define: DEBUG; TRACE/errorendlocation/preferreduilang: en-US /highentropyva -/reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\Microsoft.CSharp.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\mscorlib.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Core.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Data.DataSetExtensions.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Data.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\system.Xml.dll" /reference: "C:\Program Files (x86)\Reference Сборки \Microsoft\Framework.NETFramework\v4.0\System.Xml.Linq.dll" /debug +/debug: full/filealign: 512/optimize- /out:obj\Debug\TestConsoleApp.exe/target: exe/utf8output Program.cs Свойства \AssemblyInfo.cs "C:\Users\105044960\AppData\Local\Temp.NETFramework, Version = v4.0.AssemblyAttributes.cs" 1 > CopyFilesToOutputDirectory: 1 > Копирование файла из "obj\Debug\TestConsoleApp.exe" в "bin\Debug\TestConsoleApp.exe". 1 > TestConsoleApp → C:\Users\105044960\Documents\Visual Studio 11\Проекты\TestConsoleApp\TestConsoleApp\Bin\Debug\TestConsoleApp.exe 1 > Копирование файла из "obj\Debug\TestConsoleApp.pdb" в "Bin\Debug\TestConsoleApp.pdb".

4b9b3361

Ответ 1

Visual Studio использует встроенный компилятор, поэтому он знает, какую версию С# он использует.

Как вы отметили, csc.exe из командной строки, с другой стороны, использует любую версию С#, которую он создал для компиляции, поэтому в вашем случае это будет С# 5.0. Поскольку это обновление на месте (с точки зрения каталога установки), это может сломать код, который полагался на привязку foreach, одинаково во всем цикле (нечетное, но возможное).


ПРИМЕЧАНИЕ. Старый ответ за неправильный вопрос: OP знает об этом и тестировал его из командной строки.

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

Это компилятор, который изменился, так что это:

foreach (int value in values)
{
    // ...
}

используется для генерации чего-то по следующему коду:

{
    int value;
    for (/* iteration */)
    {
        value = /* get from enumerator */;
        // ...
    }
}

в то время как новый компилятор С# теперь генерирует эквивалент перемещения переменной внутри цикла:

for (/* iteration */)
{
    int value = /* get from enumerator */;
    // ...
}

Это имеет большое значение, так как замыкания внутри // ... будут фиксировать новую привязку value в каждом цикле вместо того, чтобы использовать ту же привязку value, которая была объявлена ​​вне цикла.

Уловка, если вы хотите, чтобы ваш код работал правильно как для старых, так и для новых компиляторов, вы должны объявить свою собственную переменную внутри цикла foreach:

foreach (int value in values)
{
    int newValue = value;
    // ...
}

Текущая спецификация С# 4.0 в Visual Studio 2010 говорит:

(...) Оператор foreach вида

foreach (V v in x) embedded-statement

затем расширяется до:

{
  E e = ((C)(x)).GetEnumerator();
  try {
      V v;
      while (e.MoveNext()) {
          v = (V)(T)e.Current;
          embedded-statement
      }
  }
  finally {
      … // Dispose e
  }
}

В спецификации С# 5.0 в Visual Studio 2012 говорится:

(...) Оператор foreach вида

foreach (V v in x) embedded-statement

затем расширяется до:

{
  E e = ((C)(x)).GetEnumerator();
  try {
      while (e.MoveNext()) {
          V v = (V)(T)e.Current;
          embedded-statement
      }
  }
  finally {
      … // Dispose e
  }
}

Ответ 2

Примечание. Я удалил большую часть своего исходного ответа. Это ответ на неправильный вопрос. Далее следует лучший ответ.

А теперь я вижу, что вы спрашиваете: "Как Visual Studio 2010 знает, как скомпилировать С# 4 вместо С# 5 после установки .NET 4.5, даже Visual Studio 2010 и Visual Studio 2012 используют один и тот же csc. exe и передать те же варианты?"

@mletterle Но .NET 4.5 - это обновление до .NET 4. Так что на моем компьютере присутствует только .NET 4. Единственная возможность заключается в том, что IDE спрятала скрытую копию компилятора .NET 4, которую я не вижу.

Я не уверен, где вы это слышали или почему вы это приняли..NET 4.5 НЕ является обновлением на месте. Это другая версия инструмента. Будут различия. Это один из них.

Обновление 1:

Похоже, мы использовали другое определение обновления на месте. Мое использование "на месте" - это "обновление, которое не должно иметь различимых различий между версиями". Определение, приведенное в статье которую вы связали с, использует ее по-другому: "на месте" в их использовании "используется одна и та же CLR, но добавляет новые библиотеки".

Так как С# 5 отличается от С# 4, это изменение НЕ "на месте" в использовании, с которым я знаком.

В результате разница заключается не в таргетинге на CLR, но в языковой версии, которую вы используете, CLR - это обновление на месте (как для версии 4.0 CLR), но язык не является (С# 4 в VS2010, С# 5 в VS2012.)

Обновление 2:

В файле .csproj(который фактически является файлом msbuild, управляемым Visual Studio), есть атрибут, который указывает целевую структуру. Проекты, выполненные с помощью Visual Studio 2012, имеют это по умолчанию:

<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>

В то время как проекты в Visual Studio 2010, ориентированные на версию 4, выглядят следующим образом:

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

Это говорит Visual Studio, чтобы настроить среду при создании для одной или другой целевой структуры. Хотя похоже, что csc.exe вызывается непосредственно из командной строки, это действительно не так: проект msbuild на самом деле является тем, что обрабатывается, и это происходит в настраиваемой рабочей среде Visual Studio.

Я могу только предположить специфику происходящего, но, вероятно, после обновления, если атрибут TargetFrameworkVersion, установленный в v4.0, возвращает среду v4.0 во время компиляции проекта, ориентированного на v4.0. С другой стороны, вызывая csc.exe из командной строки без среды, настроенной msbuild, она использует "значения по умолчанию" для своей версии (которая теперь по умолчанию соответствует С# 5), давая вам новое поведение С# 5, даже если вы 'используя командную строку VS 2010. Однако при вызове сборки через MSBuild он знает, как вернуться к исходной среде С# 4 на время сборки (поскольку MSBuild также является частью инструментальной цепочки .NET).