Почему существует разница в поведении приложения, созданного в 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".
Ответы
Ответ 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).