используя это ключевое слово в Switch в C#

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

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 ответов


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



в C# невозможно использовать ключевое слово "is" как часть оператора switch. Все метки case в коммутаторе должны оцениваться в постоянные выражения. "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


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

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

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

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


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

тип рисунка

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

   case type varname 

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


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

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


Как говорится в ответ С Микет, вы можете использовать шаблон 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;
  }
}

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


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

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

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

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

if (obj is Foo)

и

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

несмотря на название,is оператор сообщает вам, если объект совместимость с заданным типом, а не если это 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;
   }
}

затем вы can использовать switch о себе:

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