То, почему, реализовано как 'как'?

Учитывая, что это - очень естественный вариант использования (если Вы не знаете что as на самом деле делает),

if (x is Bar) {
   Bar y = x as Bar;
   something();
}

эффективно эквивалентно (то есть, сгенерированный компилятором CIL из вышеупомянутого кода будет эквивалентен) к:

Bar y = x as Bar;
if (y != null) {
    y = x as Bar; //The conversion is done twice!
    something();
}

Править:

Я предполагаю, что не ясно дал понять свой вопрос. Я никогда не писал бы второй отрывок, поскольку это, конечно, избыточно. Я утверждаю, что CIL, сгенерированный компилятором при компиляции первого отрывка, эквивалентен второму отрывку, который избыточен. Вопросы: a) Это корректно? b) Если так, почему is реализованный как этот?

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

Bar y = x as Bar;
if (y != null) {
   something();
}

ЗАКЛЮЧЕНИЕ:

Оптимизация is/as случай не является ответственностью компилятора, но JIT.

Кроме того, как с пустой проверкой это имеет меньше (и менее дорогой) инструкции, чем обе из альтернатив (is и as и is и cast).

Приложение:

CIL для как с nullcheck (.NET 3.5):

L_0001: ldarg.1
L_0002: isinst string
L_0007: stloc.0
L_0008: ldloc.0
L_0009: ldnull
L_000a: ceq
L_000c: stloc.1
L_000d: ldloc.1
L_000e: brtrue.s L_0019
L_0011: ldarg.0
L_0019: ret

CIL для и бросок (.NET 3.5):

L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: castclass string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret

CIL для и как (.NET 3.5):

L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: isinst string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret

Они были отредактированы для краткости (объявления метода, nops, и звонит во что-то () удаленный).

28
задан Peter Mortensen 26 January 2015 в 09:06
поделиться

10 ответов

a) Правильно ли это

Да, хотя я бы выразился иначе. Вы говорите, что "is" - это синтаксический сахар для as-followed-by-null-check. Я бы сказал по-другому: что "as" - это синтаксический сахар для "проверки реализации типа, приведения в случае успеха, нуля в случае неудачи".

То есть, я был бы более склонен сказать, что

if (x is Bar) { 
   Bar y = x as Bar; 
   something(); 
} 

фактически эквивалентно

if (x is Bar) { 
   Bar y = (x is Bar) ? (Bar)x : (Bar) null; 
   something(); 
} 

Видите ли, вы хотите определить "as" в терминах "is", а не наоборот. На самом деле вопрос должен звучать так: "Почему as реализовано как is?" :-)

b) Если да, то почему реализовано именно так?

Потому что это правильная реализация спецификации.

Мне кажется, я не понимаю ход ваших мыслей. Есть ли что-то неправильное в этой реализации? Как бы вы предпочли, чтобы она была реализована? В вашем распоряжении инструкции "isinst" и "castclass"; опишите кодоген для вашей программы, который вы хотели бы видеть.

12
ответ дан 28 November 2019 в 03:40
поделиться

Во-первых, я не согласен с вашим предположением, что это более типичный вариант использования. Это может быть ваш любимый подход, но идиоматический подход - стиль «as + null check»:

Bar y = x as Bar; 
if (y != null) { 
   something(); 
}

Как вы обнаружили, подход «is» требует дополнительных «as» или приведений, поэтому «as» По моему опыту, с нулевой проверкой это стандартный способ сделать это.

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

Что касается вашего фактического вопроса, почему ключевое слово is реализовано с помощью ключевого слова as , я понятия не имею, но мне нравится игра слов в вашем вопросе :) Я подозреваю, что ни один из них на самом деле не реализован в терминах другого, но инструмент (думаю, Reflector), который вы использовали для генерации C # из IL, интерпретировал IL в терминах как .

5
ответ дан 28 November 2019 в 03:40
поделиться

Ну, доступная инструкция IL (isinst) возвращает либо объект соответствующего типа, либо null, если такое преобразование невозможно. И она не выбрасывает исключение, если преобразование невозможно.

Учитывая это, и "is", и "as" тривиальны для реализации. Я бы не стал утверждать, что в данном случае "is" реализовано как "as", просто лежащая в основе IL инструкция позволяет реализовать оба варианта. Теперь, почему компилятор не может оптимизировать "is" с последующим "as" в один вызов isinst, это другой вопрос. Возможно, в данном случае это связано с областью видимости переменной (хотя к тому времени, когда это IL, область видимости на самом деле не существует)

Edit

Если подумать, вы не можете оптимизировать "is" с последующим "as" в один вызов isinst, не зная, что обсуждаемая переменная не подлежит обновлению из других потоков.

Предположим, что x - строка:

//Thread1
if(x is string)

//Thread2
x = new ComplexObject();

//Thread1
    y = x as string

Здесь y должно быть null.

9
ответ дан 28 November 2019 в 03:40
поделиться

Вы не сделаете второй y = x как Bar; , потому что у вас уже есть y, который является Bar.

2
ответ дан 28 November 2019 в 03:40
поделиться

Согласно сообщению в блоге How Many Passes? Эрика Липперта, это проход компилятора. Цитата:

Затем мы запускаем этап оптимизации, который перезаписывает тривиальные операторы is и as .

Возможно, именно поэтому вы видите один и тот же CIL, сгенерированный для обоих сниппетов.

1
ответ дан 28 November 2019 в 03:40
поделиться

Попробуйте это

SELECT DISTINCT a, b, c  
FROM t1,
(SELECT DISTINCT a,b,c FROM t2) as tt
WHERE t1.a NOT IN tt.a
AND t1.b NOT IN tt.b
AND t1.c NOT IN tt.c

Примечание: Это не было протестировано, это даже не было доказано правильно.

-121--3926328-

Вы были очень близки. Вы случайно использовали метод System.Object.Equals в выражении зависимости, а не в методе Is. Я также рекомендую при настройке общих типов, таких как последовательность или value types (int, DateTime), указать имя аргумента конструктора, чтобы избежать неоднозначности.

Вот мой тест с тем, что вы ищете:

    [TestFixture]
public class configuring_concrete_types
{
    [Test]
    public void should_set_the_configured_ctor_value_type()
    {
        const int level = 23;
        var container = new Container(x =>
        {
            x.For<Person>().Use<Doctor>();
            x.ForConcreteType<Hospital>().Configure.Ctor<int>("level").Is(level);
        });

        var hospital = container.GetInstance<Hospital>();

        hospital.Level.ShouldEqual(level);
    }
}
-121--3926339-

Вы можете написать код сейчас как

DoIfOfType<Bar>(possibleBar, b => b.something())

Что я бы сказал, было немного понятнее, но не так быстро без настоящей магии от компилятора.

1
ответ дан 28 November 2019 в 03:40
поделиться

Объем 'y' уменьшается, если вы помещаете объявление внутри цикла.

Кто бы это ни написал, вероятно, предпочитает преобразовывать «x как T» больше, чем «(T) x», и хотел ограничить объем «y».

0
ответ дан 28 November 2019 в 03:40
поделиться

Вы забыли о типах значений. Например:

    static void Main(string[] args)
    {
        ValueType vt;
        FooClass f = vt as FooClass;

    }

    private class FooClass
    {
        public int Bar { get; set; }
    }

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

0
ответ дан 28 November 2019 в 03:40
поделиться

Я сильно подозреваю, что is быстрее, чем as и не требует выделения. Итак, если x редко бывает Bar, то первый сниппет хорош. Если x чаще всего Bar, то рекомендуется as, так как в этом случае не требуется второй бросок. Это зависит от использования и обстоятельств кода.

-1
ответ дан 28 November 2019 в 03:40
поделиться

В вашем примере использование как в любом случае является избыточным. Поскольку вы уже знаете, что x - это Bar , вам следует использовать приведение:

if (x is Bar)
{
    Bay y = (Bar)x;
}

В качестве альтернативы преобразовать, используя как , и просто проверить на ноль:

Bar y = x as Bar;
if (y != null)
{

}
5
ответ дан 28 November 2019 в 03:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: