Используя ключевое слово 'is' в коммутаторе С#

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

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?

Ответы

Ответ 1

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

Ответ 3

В С# невозможно использовать ключевое слово "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

Ответ 4

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

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

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

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

Ответ 5

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

Ответ 7

В С# я считаю, что оператор switch работает только с целыми числами и строками.

Ответ 8

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

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

Танец перетасовки аргументов отменяет сужение, складывая его в вызов Dispatch - приемник знает свой точный тип и передает его, перезывая точную перегрузку процесса для своего типа. Это также большой выигрыш в производительности в таких реализациях, как .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);
    }
}

Ответ 9

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

Типовой шаблон

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

   case type varname 

Ответ 10

Есть и другая мысль, помимо того, что компилятор обрабатывает операторы 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.