使用以下代码,我在“return books”上收到“无法隐式转换”编译错误。线。
我认为,因为我要返回一个实现IPublication的图书对象列表,这应该可以正常工作吗?
public interface IPublication {}
public class Book : IPublication {}
public List<IPublication> GetBooks()
{
List<Book> books = new List<Book>();
return books;
}
我注意到,如果我将一本书作为单个IPublication对象返回,则可以正常工作。引入List<>
需要明确的演员。
作为我正在使用的解决方法:
return books.Cast<IPublication>().ToList();
答案 0 :(得分:8)
问题是List<IPublication>
可以容纳任何继承自IPublication
的类。由于编译器不知道您不会尝试将Magazine
放入GetBooks()
的结果中,因此您必须编写如下函数:
public List<IPublication> GetBooks()
{
List<IPublication> books = new List<IPublication>();
// put only books in here
return books;
}
如果你的函数返回一个不需要通过索引访问的不可变列表(并且你在C#4上),你可以这样写:
public IEnumerable<IPublication> GetBooks()
{
List<Book> books = new List<Book>();
return books;
}
如果您可以返回IEnumerable<T>
但是您正在使用C#3,那么您可以执行cdhowie建议的内容:
public IEnumerable<IPublication> GetBooks()
{
List<Book> books = new List<Book>();
return books.Cast<IPublication>();
}
如果您使用的是C#2,最好只使用我提出的第一种方法。
答案 1 :(得分:2)
这不是一个安全的转换。想象一下,如果有人将杂志放在你的书籍清单中。
public class Library
{
public List<Book> books = new List<Book>();
public List<IPublication> GetBooks()
{
return books;
}
}
其他地方,
Magazine mag = ...;
List<IPublication> pubs = someLibrary.GetBooks()
pubs.Add(mag);
在.NET 4中,您可以返回IEnumerable<IPublication>
,如here所述,而不创建任何新对象。 cdhowie还为较低版本提供了良好的解决方案。只需删除.ToList()
,然后返回IEnumerable
。
我意识到你的具体案例是安全的。但编译器无法验证。
答案 2 :(得分:1)
除非您使用的是.NET 4,否则必须使用解决方法。
考虑是否有人这样做:
public class Magazine : IPublication { }
// Somewhere...
var foo = something.GetBooks();
foo.Add(new Magazine());
如果运行时允许从List<Book>
转换为List<IPublication>
,那么您将被允许将杂志添加到书籍列表中!这打破了类型系统,因为如果某些内容仍然引用了List<Book>
,那么它肯定不会指望Magazine
在其中,它会将Magazine
视为好像一本书,然后运行时崩溃(或者更糟 - 数据损坏或不安全的事情)随之而来。
答案 3 :(得分:0)
我认为您正在使用旧版本而不是c#4.0,我们有covariance and contravariance。 所以你只能按元素转换list元素。