在c#中的开关中使用'is'关键字

时间:2008-10-21 21:52:16

标签: c# switch-statement

我目前正在为此代码添加一些新的扩展类:

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;

  }
}

并且好奇是否有办法在switch语句中使用is关键字功能?

11 个答案:

答案 0 :(得分:13)

这看起来像是一个良好的多态实现的情况。如果覆盖派生类中的相应方法,则可能根本不需要循环中的检查。

答案 1 :(得分:7)

最新版本的C#(7)现在包含此功能

Type pattern

类型模式可以实现简洁的类型评估和转换。当与switch语句一起使用以执行模式匹配时,它会测试表达式是否可以转换为指定的类型,如果可以,则将其转换为该类型的变量。它的语法是:

   case type varname 

答案 2 :(得分:5)

答案 3 :(得分:4)

在C#中,不能将“is”关键字用作switch语句的一部分。交换机中的所有案例标签必须评估为常量表达式。 “is”不能转换为常量表达式。

当谈到打开类型时,我肯定会感到痛苦。因为你所概述的解决方案确实有效,但它是x do y和a 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 :(得分:2)

虽然无法使用switch语句来检查类型,但是将问题简化为更易于管理的代码库并非不可能。

根据具体情况和要求,我会考虑。

  • 使用IDictionary<Type, T>将结果存储在字典中。 T本身可以成为你可以召集的代表。如果您不需要担心继承,这将有效 - 为继承而言需要更多的工作。

  • 在switch语句中使用类的类型名称(字符串)。这使用switch (b.GetType().Name),并且没有深度继承结构的选项。

答案 5 :(得分:2)

正如answer中的MikeT所述,您可以使用需要C#7的pattern mathing

以下是您的代码示例:

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;
  }
}

答案 6 :(得分:1)

您可以向getType()添加一个方法BaseType,该方法由每个具体子类实现,以返回唯一的整数ID(可能是枚举)并启用它,是吗?

答案 7 :(得分:1)

实际上,switch将变量(string或int(或enum))与常量表达式作为switch语句匹配。

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

答案 8 :(得分:0)

在C#中,我认为switch语句只适用于整数和字符串。

答案 9 :(得分:0)

在我的经验中,类型案例和面向对象的代码似乎并没有很好地融合在一起。在这种情况下我更喜欢的方法是double dispatch pattern。简而言之:

  • 使用空虚拟方法创建一个侦听器类型,为每个要调度的扩展类型的进程(ExtendedTypeN arg)。
  • 将虚拟方法 Dispatch(侦听器侦听器)添加到以侦听器作为参数的基本类型。它的实现将是调用listener.Process((Base)this)。
  • Over ride 每个扩展类型的Dispatch方法调用相应的 over 加载 在侦听器类型中的Process。
  • 通过覆盖您感兴趣的每个子类型的相应Process方法来扩展侦听器类型

参数shuffling dance通过将其折叠到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);
    }
}

答案 10 :(得分:-1)

除了编译器处理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.