如何在C#中将List <Interface>转换为List <Class>?

12

我有一个被定义为

public interface IReaderInfo
{
    string Displayname {get;}
}

以及实现该接口的类

public class ReaderInfo : IReaderInfo
{
    string DisplayName {get;}
}

我随后创建了一个返回List的函数

public List<ReaderInfo> GetReaders
{
     var readers = new List<ReaderInfo>();
     var Ireaders = someobject.Getreaders();// Returns the list of IReaderInfo.
     // Now i would like cast Ireaders as readers and return.
}

我该如何投掷它?


2
如果您需要将IReaderInfo强制转换为ReaderInfo,那么为什么要使用接口? - Hasan Emrah Süngü
请查看此帖子:可能是重复的。 https://stackoverflow.com/questions/18826607/cast-interface-to-a-class - Felix Almesberger
3
当你有接口时,为什么要返回具体类的列表?如果实现接口的对象不是“ReaderInfo”,那么你打算怎么办?为什么不返回“List<IReaderInfo>”? - Panagiotis Kanavos
4个回答

25
你需要创建一个包含转换后项目的新列表:
var readers = Ireaders.Cast<ReaderInfo>().ToList();

或者,如果存在不兼容的IReaderInfo条目的可能性,并且您只想在结果中获取实际的ReaderInfo对象:

var readers = Ireaders.OfType<ReaderInfo>().ToList();

1
如果元素不是“ReaderInfo”实例,则会失败。任何类都可以实现此接口。 - Panagiotis Kanavos
@RaviKumarGN 当你得到一个不是ReaderInfo的对象时会发生什么?当你尝试使用模拟IReaderInfo对象测试这段代码时会发生什么?如果GetReaders根本不返回ReaderInfo对象怎么办?它明确告诉你它不会只返回ReaderInfo。使用Cast或OfType会破坏API。 - Panagiotis Kanavos
@PanagiotisKanavos,这是否可以考虑使用 OfType 建议?我们不需要决定不同实际类型的预期以及如何处理它们 - 但我认为通过 Cast(在不同类型时抛出异常)和 OfType(过滤不同类型)已经基本覆盖了所有可能性。 - grek40
@grek40 没有什么需要决定的。该方法的保证明确说明它将返回仅具有特定接口的对象。其他任何假设都是不安全的。 - Panagiotis Kanavos
@PanagiotisKanavos 这只适用于 someobject.Getreaders,但是 GetReaders 可能有不同的契约,只需要定义和文档说明当方法被调用时数据与预期不同会发生什么即可。 - grek40
@RaviKumarGN,由于您评论说这对您有效,请考虑接受该答案。请参阅接受答案的方式 - grek40

4
快速而不太严谨的解决方法是仅传递实际上是 ReaderInfo 的对象,使用 OfType<>。如果任何对象实现了 IReaderInfo 但实际上并不是 ReaderInfo,则使用 Cast<> 将失败。
var Ireaders = someobject.Getreaders();
var readers=Ireaders.OfType<ReaderInfo>().ToList();
return readers;

更好的解决方案是将该方法的签名更改为返回List<IReaderInfo>或更抽象的IList<IReaderInfo>。如果GetReaders返回一个接口列表,它意味着它可以返回实现该接口的任何对象。
为什么要丢弃一些对象?当所有对象都实现相同的接口时,为什么应该由GetReaders决定使用哪些对象和丢弃哪些对象?
public IList<IReaderInfo> GetReaders
{
    var readers = new List<ReaderInfo>();
    var Ireaders = someobject.Getreaders();// Returns the list of IReaderInfo.
    return Ireaders;
}

例如,当方法有意返回不同类型(如模拟对象或处理多个实现)时,通常会返回接口。通过返回 List<ReaderInfo>,您可以防止方法处理有效的对象。
更新
版本控制是另一个非常强的理由,为什么不应将接口转换为具体类型。
在搜索 IReaderInfo 时,我发现了几个页面与 RFID 读取器相关。ReaderInfo 返回读取器的功能,但如何为新读取器公开新功能?
一种在不破坏兼容性的情况下公开新功能的常见方式是引入一个实现旧接口 IReaderInfo 的新类,并通过新接口(例如 IReaderInfo2)公开新功能。或者可以使用 IReaderInfo2 扩展现有类。库可以这样做,因为客户端预期只通过接口而不是直接针对具体类工作。

如果客户端仅通过接口工作,那为什么要公开类呢?如果没有公共类,从接口到类的强制转换问题将永远不会发生在库的任何用户身上。 - grek40
出于同样的原因,BCL中的许多方法都公开接口而不是具体类。版本控制只是其中之一。可测试性是另一个原因——BCL团队最大的遗憾之一就是过度硬编码类。 - Panagiotis Kanavos

1

使用LINQ:

Ireaders.Cast<ReaderInfo>().ToList();

Ireaders.OfType<ReaderInfo>().Cast<ReaderInfo>().ToList() 应该满足所有的注释。 - brijber
有时在 Cast<> 中抛出异常比用 OfType<> 静默忽略源集合中的意外项更好。这取决于上下文和要求/规范,而作者没有指定细节,因此我的回答仅涉及转换。 - Mykola Kovalchuk

0

通过使用 Linq

var derivedList = Ireaders.Cast<ReaderInfo>();

1
如果任何对象实现了接口但不是实际的IReaderInfo,则会抛出异常。 - Panagiotis Kanavos
1
抛出异常不是一个好主意,而这段代码就是这样做的。 - Panagiotis Kanavos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接