Различия между тем, как C # и VB обрабатывают именованные параметры? - PullRequest
19 голосов
/ 25 февраля 2010

Теперь, когда 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.

<ч />

EDIT : Теперь, когда мы определили, что дополнительные инструкции исчезают в режиме выпуска, есть ли какая-то особая причина их присутствия в режиме отладки? VB действует одинаково в обоих режимах, и C # не вставляет дополнительные инструкции при обычном вызове метода без именованных параметров (в том числе при использовании необязательных параметров).

Ответы [ 2 ]

13 голосов
/ 25 февраля 2010

есть ли особая причина, по которой они присутствуют в режиме отладки?

Разница между:

  • поместите временное значение в стек, используйте его, отбросьте его и
  • сохранить временное значение в определенном слоте стека, сделать его копию в стеке, использовать его, выбросить, но оригинальная копия временного значения остается в слоте стека

Видимый эффект этой разницы заключается в том, что сборщик мусора не может быть настолько агрессивным в очистке значения. В первом сценарии значение может быть собрано сразу после возврата вызова. Во втором сценарии значение собирается только после возврата текущего метода (или повторного использования слота).

Ослабление сборщика мусора часто помогает в сценариях отладки.

Подразумеваемый вопрос:

Почему разница между 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 и R. слева направо. Это разумное внутреннее представление, и когда мы кодируем его в неоптимизированном режиме, мы просто кодируем код непосредственно из внутреннее представление практически без изменений.

Когда мы запускаем оптимизатор, мы обнаруживаем все виды вещей - например, тот факт, что никто не использует эти невидимые локальные переменные для чего-либо. Затем мы можем исключить местных жителей из кодагена.

Я очень мало знаю о внутреннем представлении VB; Я не работал над компилятором VB с 1995 года, и я слышал, что он мог немного измениться за последние пятнадцать лет. Я мог бы предположить, что они делают нечто подобное, но я не знаю деталей того, как они представляют именованные параметры или как их генератор кода работает с ними.

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

5 голосов
/ 25 февраля 2010

Компилятор создает следующий код 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 
}

Так идентично.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...