Ответ 1
Это действительно похоже на ситуацию для хорошей полиморфной реализации. Если вы переопределите соответствующие методы в производных классах, вам могут не потребоваться проверки в цикле вообще.
В настоящее время я добавляю несколько новых расширенных классов в этот код:
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?
Это действительно похоже на ситуацию для хорошей полиморфной реализации. Если вы переопределите соответствующие методы в производных классах, вам могут не потребоваться проверки в цикле вообще.
Неа. См
В С# невозможно использовать ключевое слово "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
Пока невозможно использовать оператор switch для проверки типов, невозможно свести проблему к более управляемой базе кода.
В зависимости от конкретной ситуации и требований, которые я бы рассмотрел.
Использование IDictionary<Type, T>
для хранения результата в словаре. T сам может быть делегатом, на который вы можете позвонить. Это будет работать, если вам не нужно беспокоиться о наследовании - для обеспечения наследования потребуется немного больше работы.
Использование имени типа класса (который является строкой) внутри оператора switch. Это использует switch (b.GetType().Name)
, и нет возможности для глубокой структуры наследования.
Вы можете добавить метод getType()
в BaseType
, который реализуется каждым конкретным подклассом, чтобы вернуть уникальный интегральный идентификатор (возможно, перечисление) и включить его, да?
Не совсем, переключатели соответствуют переменной (string или int (или enum)) с константным выражением в качестве оператора switch.
http://msdn.microsoft.com/en-us/library/06tc147t(VS.71).aspx
В С# я считаю, что оператор switch работает только с целыми числами и строками.
Типовые и объектно-ориентированные коды, похоже, не так хорошо сочетаются в моем опыте. Подход, который я предпочитаю в этой ситуации, это двойной шаблон отправки. Короче говоря:
Танец перетасовки аргументов отменяет сужение, складывая его в вызов 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);
}
}
В последней версии С# (7) теперь включена эта функциональность
Шаблон типа обеспечивает краткий анализ и преобразование типа. Когда используется оператор switch для выполнения сопоставления с образцом, он проверяет, может ли выражение быть преобразовано в указанный тип, и, если возможно, переводит его в переменную этого типа. Его синтаксис:
case type varname
Есть и другая мысль, помимо того, что компилятор обрабатывает операторы 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.