используя ключевое слово is в переключателе в c # - PullRequest
15 голосов
/ 22 октября 2008

В настоящее время я добавляю несколько новых расширенных классов в этот код:

foreach (BaseType b in CollectionOfExtendedTypes) {
  if (b is ExtendedType1) {
    ((ExtendedType1) b).foo = this;

  }
  else if (b is ExtendedType2) {
    ((ExtenedType2) b).foo = this;

  } 
  else {
    b.foo = this;

  }
}

и было бы любопытно, если есть способ использовать функцию ключевого слова is в операторе switch?

Ответы [ 11 ]

13 голосов
/ 22 октября 2008

Это действительно похоже на ситуацию для хорошей полиморфной реализации. Если вы переопределите соответствующие методы в производных классах, вам может вообще не понадобиться проверка в цикле.

7 голосов
/ 30 ноября 2017

Последняя версия C # (7) теперь включает эту функцию

Тип рисунка

Шаблон типа позволяет проводить краткую оценку и преобразование типов. При использовании с оператором switch для сопоставления с образцом он проверяет, можно ли преобразовать выражение в указанный тип, и, если это возможно, преобразует его в переменную этого типа. Его синтаксис:

   case type varname 
5 голосов
/ 22 октября 2008
4 голосов
/ 22 октября 2008

В C # невозможно использовать ключевое слово "is" как часть оператора switch. Все метки регистра в переключателе должны соответствовать константным выражениям. «is» не может быть преобразовано в постоянное выражение.

Я определенно чувствую боль, когда дело доходит до переключения типов. Потому что на самом деле решение, которое вы обрисовали в общих чертах, работает, но это условное выражение для x do y и a do b. Было бы гораздо более естественным написать это более похоже на следующее


TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

Вот сообщение в блоге, которое я написал о том, как добиться этой функциональности.

http://blogs.msdn.com/jaredpar/archive/2008/05/16/switching-on-types.aspx

2 голосов
/ 06 апреля 2018

Как уже упоминалось в ответе из MikeT , вы можете использовать pattern mathing , для которого требуется C # 7.

Вот пример для вашего кода:

foreach (BaseType b in CollectionOfExtendedTypes) {
  switch (b) {
    case ExtendedType1 et1:
        // Do stuff with et1.
        et1.DoStuff();
        break;
    case ExtendedType2 et2:
        // Do stuff with et2.
        et2.DoOtherStuff();
        break;
    default:
        // Do something else...
        break;
  }
}
2 голосов
/ 22 октября 2008

Хотя невозможно использовать оператор switch для проверки типов, все же невозможно уменьшить проблему до более управляемой базы кода.

В зависимости от конкретной ситуации и требований, которые я рассмотрю.

  • Использование IDictionary<Type, T> для сохранения результата в словаре. Сам T может быть делегатом, которого вы можете вызвать. Это сработает, если вам не нужно беспокоиться о наследовании - обслуживание наследства займет немного больше работы.

  • Использование имени типа класса (который является строкой) внутри оператора switch. При этом используется switch (b.GetType().Name), и нет возможности для глубокой структуры наследования.

1 голос
/ 22 октября 2008

Не совсем, переключатели соответствуют переменной (string или int (или enum)) с константным выражением в качестве оператора switch.

http://msdn.microsoft.com/en-us/library/06tc147t(VS.71).aspx

1 голос
/ 22 октября 2008

Вы можете добавить метод getType() к BaseType, который реализуется каждым конкретным подклассом, чтобы вернуть уникальный интегральный идентификатор (возможно, enum) и включить его, да?

0 голосов
/ 16 января 2009

Типовые случаи и объектно-ориентированный код, кажется, не очень хорошо сочетаются в моем опыте. Подход, который я предпочитаю в этой ситуации, - шаблон двойной отправки . Короче говоря:

  • Создайте тип слушателя с пустым виртуальным методом Process (ExtendedTypeN arg) для каждого расширенного типа, который вы будете отправлять.
  • Добавление виртуального метода Dispatch (слушатель слушателя) к базовому типу, который принимает слушатель в качестве аргумента. Его реализация будет вызывать listener.Process ((Base) this).
  • Over ride метод Dispatch в каждом расширенном типе для вызова соответствующего over load Process в типе слушателя.
  • Расширьте тип слушателя , переопределив соответствующий метод Process для каждого интересующего вас подтипа.

Аргумент перетасовки танца устраняет сужающее приведение, складывая его в вызов Dispatch - получатель знает его точный тип и передает его, вызывая точную перегрузку Process для его типа. Это также большой выигрыш в производительности в таких реализациях, как .NET Compact Framework, в которых сужение выполняется чрезвычайно медленно, а виртуальная диспетчеризация - быстрая.

Результат будет примерно таким:


public class Listener
{
    public virtual void Process(Base obj) { }
    public virtual void Process(Derived obj) { }
    public virtual void Process(OtherDerived obj) { }
}

public class Base
{
    public virtual void Dispatch(Listener l) { l.Process(this); }
}

public class Derived
{
    public override void Dispatch(Listener l) { l.Process(this); }
}

public class OtherDerived
{
    public override void Dispatch(Listener l) { l.Process(this); }
}

public class ExampleListener
{
    public override void Process(Derived obj)
    {
        Console.WriteLine("I got a Derived");
    }

    public override void Process(OtherDerived obj)
    {
        Console.WriteLine("I got an OtherDerived");
    }

    public void ProcessCollection(IEnumerable collection)
    {
        foreach (Base obj in collection) obj.Dispatch(this);
    }
}
0 голосов
/ 22 октября 2008

Есть еще одна вещь, о которой следует подумать, кроме способа, которым компилятор обрабатывает операторы switch, и это функционирование оператора is. Есть большая разница между:

if (obj is Foo)

и

if (obj.GetType() == typeof(Foo))

Несмотря на имя, оператор is сообщает вам, является ли объект совместимым с данным типом, а не если равен данного типа. Это приводит к не совсем очевидным ошибкам (хотя это довольно очевидно), которые выглядят так:

if (obj is System.Object)
{
   //this will always execute
}
else if (obj is Foo)
{
   //this will never execute
}

Многие из приведенных здесь советов указывают вам, как использовать тип объекта. Это хорошо, если вам действительно нужна логика, связанная с каждым типом. Но если это так, будьте осторожны при использовании оператора is.

Также: хотя вы не можете изменять эти базовые типы, это не значит, что вы не можете использовать предложение Оуэна. Вы можете реализовать методы расширения:

public enum MyType { Foo, Bar, Baz };
public static class MyTypeExtension
{
   public static MyType GetMyType(this Foo o)
   {
      return MyType.Foo;
   }
   public static MyType GetMyType(this Bar o)
   {
      return MyType.Bar;
   }
   public static MyType GetMyType(this Baz o)
   {
      return MyType.Baz;
   }
}

Тогда вы можете использовать выражение switch:

switch (myObject.GetType())
{
   case MyType.Foo:
     // etc.
...