测试对象是否实现接口有什么问题?

Gen*_*e C 30 c# oop polymorphism

这个答案的评论中,声明" 检查对象是否实现了界面 ,尽管可能是猖獗的,但这是件坏事 "

以下是我认为这种做法的一个例子:

public interface IFoo
{
   void Bar();
}

public void DoSomething(IEnumerable<object> things)
{
   foreach(var o in things)
   {
       if(o is IFoo)
           ((IFoo)o).Bar();
   }
}
Run Code Online (Sandbox Code Playgroud)

由于我的好奇心激起了以前曾使用过这种模式的变化,我搜索了一个很好的例子或解释为什么它是一件坏事并且无法找到它.

虽然我很可能误解了评论,但有人可以提供一个示例或链接来更好地解释评论吗?

Jon*_*eet 36

这取决于你想要做什么.有时它可能是合适的 - 例子包括:

  • LINQ to Objects,用于优化操作Count,可以IList<T>通过专用成员更高效地执行操作.
  • LINQ to XML,它用于提供一个非常友好的API,它接受各种类型,在适当的地方迭代值
  • 如果要在Windows窗体中的特定控件下查找特定类型的所有控件,您可能需要检查每个控件是否是一个容器,以确定是否递归.

在其他情况下,它不太合适,您应该考虑是否可以更改参数类型.这绝对是一种"嗅觉" - 通常你不应该关注任何交给你的实施细节; 您应该只使用声明的参数类型提供的API.这也被称为违反Liskov替代原则.

无论教条开发商周围可能会说,有些时候,你根本需要检查对象的执行时间类型.object.Equals(object)没有使用is/ as/ 很难正确覆盖GetType,例如:)它并不总是坏事,但它应该总是让你考虑是否有更好的方法.谨慎使用,只有在真正最合适的设计的地方使用.


我个人宁愿写下你这样看过的代码,请注意:

public void DoSomething(IEnumerable<object> things)
{
    foreach(var foo in things.OfType<IFoo>())
    {
        foo.Bar();
    }
}
Run Code Online (Sandbox Code Playgroud)

它完成了同样的事情,但以更整洁的方式:)


Ern*_*rno 18

我希望这个方法看起来像这样,看起来更安全:

public void DoSomething(IEnumerable<IFoo> things)
{
   foreach(var o in things)
   {
           o.Bar();
   }
}
Run Code Online (Sandbox Code Playgroud)

阅读有关Liskov原则的违反行为:什么是Liskov替代原则?


Eri*_*ert 10

如果你想知道评论者为何发表评论,可能最好请他们解释.

我不认为你发布的代码是"坏".更"真实"的不良做法是使用接口作为标记.也就是说,你没有计划实际使用接口的方法; 相反,您已经在类上声明了接口,以某种方式描述它.使用属性而不是接口作为类的标记.

标记接口在许多方面都是危险的.我曾经遇到过一个真实的情况,一个重要的产品在标记界面的基础上做出了错误的决定:http://blogs.msdn.com/b/ericlippert/archive/2004/04/05/108086的.aspx

也就是说,C#编译器本身在一种情况下使用"标记接口".Mads在这里讲述了这个故事:http://blogs.msdn.com/b/madst/archive/2006/10/10/what-is-a-collection_3f00_.aspx


koe*_*oen 6

原因在于,如果不挖掘代码,就会对该接口产生依赖关系.