Теперь, когда C# поддерживает названные параметры, я проверял, чтобы видеть, был ли он реализован тем же путем, VB сделал это и нашел, что существуют незначительные различия. Возьмите, например, библиотечную функцию как это:
public static void Foo(string a, string b)
{
Console.WriteLine(string.Format("a: {0}, b: {1}", a, b));
}
В C#, если Вы называете его как это:
Foo(a: "a", b: "b");
Компилятор производит следующие инструкции IL:
.locals init (
[0] string CS$0$0000,
[1] string CS$0$0001)
L_0000: nop
L_0001: ldstr "a"
L_0006: stloc.0
L_0007: ldstr "b"
L_000c: stloc.1
L_000d: ldloc.0
L_000e: ldloc.1
L_000f: call void [TestLibrary]TestLibrary.Test::Foo(string, string)
L_0014: nop
L_0015: ret
Который переводит в следующий код C#:
string CS$0$0000 = "a";
string CS$0$0001 = "b";
Test.Foo(CS$0$0000, CS$0$0001);
В VB, если Вы называете его как это:
Foo(a:="a", b:="b")
Компилятор производит следующие инструкции IL:
L_0000: nop
L_0001: ldstr "a"
L_0006: ldstr "b"
L_000b: call void [TestLibrary]TestLibrary.Test::Foo(string, string)
L_0010: nop
L_0011: nop
L_0012: ret
Который переводит в следующий код VB:
Foo("a", "b");
Путем VB делает требуется намного меньше вызовов инструкции, так есть ли какое-либо преимущество для способа, которым C# реализует его? Если Вы не используете именованные параметры, C# производит то же самое как VB.
Править: Теперь, когда мы решили, что дополнительные инструкции уходят в режиме выпуска, есть ли конкретная причина для них присутствовать в режиме отладки? Действия VB, которые то же в обоих режимах и C# не вставляют дополнительные инструкции при вызове метода обычно без именованных параметров (включая то, когда Вы используете дополнительные параметры).
есть ли особая причина для их присутствия в режиме отладки?
Разница между:
Видимый эффект этого различия заключается в том, что сборщик мусора не может так агрессивно очищать значение. В первом сценарии значение может быть получено сразу после возврата вызова. Во втором сценарии значение собирается только после возврата из текущего метода (или после повторного использования слота).
Уменьшение агрессивности сборщика мусора часто помогает в сценариях отладки.
Подразумевается вопрос:
Почему разница между C # и VB?
Компиляторы C # и VB были написаны разными людьми, которые сделали свой выбор в отношении того, как работают их соответствующие генераторы кода.
ОБНОВЛЕНИЕ: Re: ваш комментарий
В компиляторе C # неоптимизированная генерация IL по существу имеет ту же структуру, что и наше внутреннее представление функции. Когда мы видим именованный аргумент:
M(y : Q(), x : R());
, где метод, скажем,
void M(int x, int y) { }
, мы представляем его внутренне, как если бы вы написали
int ytemp = Q();
int xtemp = R();
M(xtemp, ytemp);
, потому что мы хотим сохранить оценку побочных эффектов Q слева направо. и Р.Это разумное внутреннее представление, и когда мы кодируем его в неоптимизированном режиме, мы просто кодируем код непосредственно из внутреннего представления без каких-либо модификаций.
Когда мы запускаем оптимизатор, мы обнаруживаем всевозможные вещи - например, тот факт, что никто не использует эти невидимые локальные переменные ни для чего. Затем мы можем исключить местных жителей из кодогенератора.
Я очень мало знаю о внутреннем представлении VB; Я не работал над компилятором VB с 1995 года и слышал, что за последние пятнадцать лет он мог немного измениться. Я бы предположил, что они делают что-то подобное, но я не знаю подробностей того, как они представляют именованные параметры или как их генератор кода работает с ними.
Я считаю, что это различие, насколько мне известно, не иллюстрирует важное семантическое различие. Скорее, это показывает, что неоптимизированная сборка просто выплевывает любое высокоуровневое внутреннее представление, которое мы создали, и которое, как мы знаем, имеет желаемую семантику.
Компилятор создает следующий код C #:
Нет - компилятор создает IL, который затем переводится в C #. Любое сходство с кодом C # чисто случайно (и не весь сгенерированный IL может быть записан как C #). Наличие всех этих «nop» говорит мне, что вы находитесь в режиме «отладки». Я бы попробовал еще раз в режиме «выпуска» - это может иметь большое значение для этих вещей.
Я запустил его в режиме выпуска, используя:
static void Main()
{
Foo(a: "a", b: "b");
}
Раздача:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 8
L_0000: ldstr "a"
L_0005: ldstr "b"
L_000a: call void ConsoleApplication1.Program::Foo(string, string)
L_000f: ret
}
Так идентично.