【问题标题】:C# Downcasting Lists to IList "special tactics"C# 将列表向下转换为 IList “特殊策略”
【发布时间】:2012-01-12 22:38:52
【问题描述】:

所以我在一家公司工作,我在这里相对较新,但我在一些生产代码中遇到了一些非常奇怪的东西,我听说这些代码是由比我自己更懂 C# 的人编写的事情有特殊的方式”。所以我试图想出理由去做我将要解释的事情,给出怀疑的好处,但我什么都想不出来。

IList<Facility> Facilities = new List<Facility>();

上面的代码是问题的核心......整个代码中的其他奇怪之处在于无法对某些事物使用接口,因此在使用它们之前必须将数据复制回列表中(例如通过webservice导出,不允许使用接口,必须是IList的实现)

现在我明白接口很棒,因为您可以编写方法,期望接口中规定的方法存在于传递给您的方法的实现中,但是......

有人能帮帮我吗...谁能想到你会立即将列表向下转换回应用程序数据层中的接口的原因?

也只是为了明确...这些列表也不用于可互换的数据类型,这发生在定义和存储 List 的地方,并且每个存储的数据类型都有一个 IList。

谢谢!

【问题讨论】:

    标签: c#


    【解决方案1】:

    使用层次结构中可能最高的类被认为是一种很好的做法。因此,如果您只需要遍历值,则应该只公开IEnumerable<T>

    IEnumerable<Facility> Facilities = new List<Facility>();
    // now you can foreach over the facilities or chain with LINQ
    

    如果您需要索引器访问权限,您可以使用IList&lt;Facility&gt;

    IList<Facility> Facilities = new List<Facility>();
    // in addition to looping now you can access facilities by index 
    // and you can also add and remove facilities
    

    在设计某些 API 时公开层次结构中可能最高的类/接口背后的想法是,这样您就隐藏了您使用 List&lt;T&gt; 的实现细节。如果你明天发明一个HyperSpecialListWhichIsVeryFast&lt;T&gt; 来实现你与API 消费者达成的合同,那么这个消费者不需要更改他的代码中的任何内容。他不在乎。他与IEnumerable&lt;T&gt; 合作,因为他所需要的只是循环。并且由于您的新列表,他的代码将运行得更快,而无需他进行任何更改。

    但是现在使用历史书籍中的非泛型 à la .NET 1.0 弱类型集合就像巫术魔法:

    IList Facilities = new List<Facility>();
    

    想不出这有用的任何理由。向下转换为非泛型版本将强制客户端转换以访问底层类型T。这样他就失去了泛型提供的所有编译时安全性。

    【讨论】:

    • 奇怪的是他们向下转换为 IList 而不是 IList();
    • 同意!我也总是使用模板版本而不是通用版本!
    • @ivowiblo,是的,这很奇怪。想不出一个很好的理由为什么要这样做。
    • 就像@Darin 所说,如果一个方法返回 IEnumerable 那么任何实现这个接口的类都可以使用结果。
    • 嗯,我的回答是“向下转换为非泛型类型没有任何意义,除非您将其公开在特定接口、抽象或虚拟方法实现中”
    【解决方案2】:

    这完全取决于您打算如何使用您的 Facilities 变量。如果您只打算将其作为IList 进行操作(您只打算调用该接口中定义的方法),则可以合理地声明该变量以强制执行您不尝试调用方法的约束在该接口上定义。这样,您可以轻松地将您使用的数据结构替换为 IList 的另一个实现。

    我自己,除非我有特殊需要,否则我通常会避免使用非通用版本。 (即我会使用IList&lt;Facility&gt; 而不是IList)使用非泛型版本(在您的代码中的其他位置)的一种可能情况是,它显然不要求其他代码知道任何关于Facility 的信息 - - 它可以作为对象的一般列表对列表进行操作。

    【讨论】:

    • 恕我直言,太糟糕了IList&lt;T&gt; 不继承非泛型IList;如果非通用版本是只读实现,它可以安全地执行此操作,并且这样做将使接受IEnumerable&lt;Animal&gt; 的代码可以访问传入IList&lt;Zebra&gt;Count 属性。
    • 这是问题所在...所有内容都存储为 IList 但是在代码中的其他位置(例如用于在 Web 服务上导出),数据会从 IList 中复制出来 回到 List
    【解决方案3】:

    如果一个接口包含一个类的所有成员,那么在每个使用该类的地方都使用该接口并没有太大的缺点,唯一的例外是构造函数调用和静态方法(其中一个必须使用类(*))。使用接口而不是类的最大问题也是这样做的最大原因:指定接口的代码通常会与使用类的代码尴尬地接口;如果将来有可能需要使用该接口,那么从一开始就这样做可以避免以后进行可能尴尬的重构工作。

    (*)如果我有我的 druthers,一个接口可以指定一个用于构造函数或静态方法的类,这样如果声明接口 IFoo 指定它与 Foo '合作',那么 IFoo.Bar() 将等同于Foo.Bar()new IFoo(3) 将等效于 new Foo(3)'。不幸的是,不存在这样的功能;如果是这样,我怀疑接口的使用会更加一致。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-02
      • 1970-01-01
      • 1970-01-01
      • 2017-03-08
      • 1970-01-01
      相关资源
      最近更新 更多