используя это ключевое слово в 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;
}
}
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.