Выражение типа "is" для проверки на ноль - PullRequest
6 голосов
/ 11 апреля 2019

Я могу реорганизовать этот код (популярный as/null check шаблон)

var a = b as MyType;
if(a != null) { ... }

.. в хорошее "is" выражение шаблона типа :

if(b is MyType a) { ... }

.. что круто ... я думаю ... это так?


Но теперь я тоже думаю о рефакторинге

var a = SomeMethod();
if(a != null) { ... }

.. в:

if(SomMethod() is MyType a) { ... }

Примечание: as отсутствует и SomeMethod () уже возвращает MyType .Это похоже на (псевдокод) if(A is A) и может легко запутать, нет?

Первый рефакторинг является законным, но как насчет последнего?Я не эксперт по IL, чтобы проверить себя, и функции C # 7.0 все еще являются новыми для меня.Возможно, есть проблемы, которые я еще не обнаружил?

Ответы [ 3 ]

4 голосов
/ 12 апреля 2019

Очевидно, что 2 реализации очень похожи, разница будет незначительной в памяти , выделениях и циклах .

Компилятор в основном обрабатывает их следующим образом (для ссылочных типов)

First

MyType myType = SomeMethod();
if (myType != null)
{
   Console.WriteLine(myType.ToString());
}

Второй

MyType myType2;
if ((object)(myType2 = SomeMethod()) != null)
{
   Console.WriteLine(myType2.ToString());
}

Вероятно, лучше видно с IL

Первый

IL_0000: ldarg.0
IL_0001: call instance class C/MyType C::SomeMethod()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0015

IL_000a: ldloc.0
IL_000b: callvirt instance string[mscorlib] System.Object::ToString()
IL_0010: call void[mscorlib] System.Console::WriteLine(string)

Второй

IL_0015: ldarg.0
IL_0016: call instance class C/MyType C::SomeMethod()
IL_001b: dup
IL_001c: stloc.1
IL_001d: brfalse.s IL_002a

IL_001f: ldloc.1
IL_0020: callvirt instance string[mscorlib] System.Object::ToString()
IL_0025: call void[mscorlib] System.Console::WriteLine(string)

Примечание : вы можете проверить разборку, IL и jit-asm здесь

Разница IL в основном составляет 2 кода операции:

  • dup: копирует текущее верхнее значение в стек оценки, а затем помещает копию в стек оценки.
  • Ldloc: загружает локальную переменную с определенным индексом в стек оценки.

Когда соединено , наиболее вероятно оптимизирует в те же инструкции в любом случае


Резюме

  1. Нет заметных технических отличий.
  2. Да, версия is немного более аккуратная и более краткая, я думаю.
  3. Это, вероятно, больше пригодных для печати фрахтователей, так что если у вас есть печатаемый персонаж OCD или вы столкнулись с брутальными кодами, это может быть не очень хорошо
  4. Если вам это нравится, и вашей команде это нравится, продолжайте.
  5. Это не совсем моя чашка чая
1 голос
/ 12 апреля 2019

Я нашел компилятор очень умный.Существует несколько вариантов перевода для выражения is:

if(SomeMethod() is MyType a) {...}

  1. SomeMethod возвращает MyType

    • MyType не имеет оператора переопределения ==, а переменная a не используется

      if (SomeMethod() != null) {...}
      
    • MyType имеет оператор переопределения ==, но переменная a не используется

      if ((object)(SomeMethod()) != null) {...}
      
    • MyType не имеет оператора переопределения ==, и используется переменная a

      MyType a;
      if ((a = SomeMethod()) != null) {...}
      
    • MyTypeимеет оператор переопределения ==, и используется переменная a

      MyType a;
      if ((object)(a = SomeMethod()) != null) {...}
      
  2. SomeMethod возвращает другой тип, например object

    • Переменная a не используется

      if (SomeMethod() is MyType) {...}
      
    • MyType не имеет оператора переопределения ==, а переменная a используется

      MyType a;
      if ((a = (SomeMethod() as MyType)) != null) {...}
      
    • MyType имеет оператор переопределения ==, и используется переменная a

      MyType a;
      if ((object)(a = (SomeMethod() as MyType)) != null) {...}
      

Кстати, вы можетепроверьте все эти варианты с помощью ILSpy или чего-то подобного.

0 голосов
/ 12 апреля 2019

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

Для типов, допускающих значения NULL, это совсем другая история.Учитывая struct S, тогда

void foo(S? p)
{
    if (p is S s) {
        bar(s);
    }
}

эквивалентно

void foo(S? p)
{
    if (p.HasValue) {
        bar(p.GetValueOrDefault());
    }
}

и избегает вызова GetValueOrDefault() (или, что еще хуже, чтения свойства Value, которое выполняет еще одна проверка на ноль) - это очень полезная IMO, которая значительно улучшает читабельность.

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