我正在做一个.NET MVC教程。话虽如此,我遇到过这样的代码:
public class MoviesController : Controller
{
public ActionResult Index()
{
var movies = GetMovies();
return View(movies);
}
private IEnumerable<Movie> GetMovies()
{
return new List<Movie>
{
new Movie {Id = 1, Name = "Shrek"},
new Movie {Id = 2, Name = "LotR"}
};
}
}
电影的索引视图如下:
@model IEnumerable<VideoStore.Models.Movie>
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Movies</h2>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Movie</th>
</tr>
</thead>
<tbody>
@foreach (var movie in Model)
{
<tr>
<td>@movie.Name</td>
</tr>
}
</tbody>
</table>
所以我的问题是,为什么在私有方法MoviesController
的{{1}}中使用GetMovies()
返回类型?为什么不使用IEnumerable
返回类型?
答案 0 :(得分:3)
IEnumerable<>
是与遍历List<>
有关的接口。返回List<>
会立即向GetMovies()
的使用者显示比严格必要的更多操作(例如,从集合中添加或删除集合),这会使引入错误更加容易。没有技术上的理由选择IEnumerable<>
而不是List<>
,因为在幕后它的表现将相同。该决定纯粹是实际的。
答案 1 :(得分:3)
使用IEnumerable会削弱耦合,从而在实现发生更改时使其与其他具体类型向前兼容。无需更改界面即可进行以下所有更改:
//Original
private IEnumerable<Movie> GetMovies()
{
return new List<Movie>
{
new Movie {Id = 1, Name = "Shrek"},
new Movie {Id = 2, Name = "LotR"}
};
}
//Using an array
private IEnumerable<Movie> GetMovies()
{
return new Movie[]
{
new Movie {Id = 1, Name = "Shrek"},
new Movie {Id = 2, Name = "LotR"}
};
}
//From EF
private IEnumerable<Movie> GetMovies()
{
return dbContext.Movies.Where( m => Name == "Shrek" || Name == "Lotr );
}
//Covariant
class NerdMovie : Movie {}
private IEnumerable<Movie> GetMovies()
{
return new List<NerdMovie>
{
new NerdMovie {Id = 1, Name = "Shrek"},
new NerdMovie {Id = 2, Name = "LotR"}
};
}
//Custom type
class MovieList : List<Movie> { }
private IEnumerable<Movie> GetMovies()
{
return new MovieList
{
new Movie {Id = 1, Name = "Shrek"},
new Movie {Id = 2, Name = "LotR"}
};
}
//Using yield
private IEnumerable<Movie> GetMovies()
{
yield return new Movie {Id = 1, Name = "Shrek"};
yield return new Movie {Id = 2, Name = "LotR"};
}
答案 2 :(得分:3)
返回类型为IEnumerable而不只是List吗?
//controller
var a = new Dictionary<string, string>();
return View(a);
var b = new List<string>();
return View(b);
var c = new LinkedList<string>();
return View(c);
// All work with:
@model IEnumerable<string>
虽然使用IEnumerable<>
更容易实现 Open sOlid Principles,但我很少建议将任何集合类型的接口/类传递给视图。
在C#数组/集合中,First-Class Citizens的问题在于,它们在维护Single Responsibility Principle时不可扩展。
例如:
// Controller Returns:
var people = .... as IEnumerable<Person>;
return View(people);
@model IEnumerable<Person>
现在假设要向视图添加与该组无关的任何信息(例如页面标题)..您该怎么做?您可以扩展并创建自己的类,该类派生自IEnumerable<T>
,但由于页面的标题与一群人无关,因此破坏了SRP。相反,您应该创建一个代表视图所需内容的一流模型:
public class MyViewModel
{
public string Title { get; set;}
public IEnumerable<Person> People { get; set;}
}
return View(myViewModel);
@model MyViewModel
我建议总是这样做。一旦您开始在MVC中使用部分或模板,或者想回发同一对象,则由于需要更改部分和/或模板和/或Javascript,离开IEnumerable<>
变得越来越困难。
所以我的问题是,为什么在私有方法
MoviesController
的{{1}}中使用GetMovies()
返回类型?
一般来说,最好使用Program against an Interface and not an Implementation,也称为Design by contract (DbC), also known as contract programming, programming by contract and design-by-contract programming,。
这是一个soLid Principle特别是Liskov替代原则。摘录:
可替换性是面向对象编程中的一个原则,该原则指出,在计算机程序中,如果S是T的子类型,则可以用类型S的对象替换类型T的对象(即,类型T的对象可以是类型T的对象)。替换为子类型S的任何对象,而无需更改程序的任何所需属性(正确性,执行的任务等)。更正式地讲,Liskov替换原理(LSP)是子类型关系的一种特殊定义,称为(强)行为子类型,最初由Barbara Liskov在1987年的会议主题演讲中以数据抽象和层次结构引入。它是一种语义关系,而不仅仅是句法关系,因为它旨在保证层次结构中的类型(尤其是对象类型)的语义互操作性。芭芭拉·里斯科夫(Barbara Liskov)和珍妮特·温(Jeannette Wing)在1994年的一篇论文中简要地描述了该原理,如下所述。
实际上,它表示当前代码:
IEnumerable<>
可以更改为:
public ActionResult Index()
{
var movies = GetMovies();
return View(movies);
}
private IEnumerable<Movie> GetMovies()
{
return new List<Movie>
{
new Movie {Id = 1, Name = "Shrek"},
new Movie {Id = 2, Name = "LotR"}
};
}
现在我们不知道如何检索电影...只要合同/接口满足我们的数据需求,我们就不必在意。
答案 3 :(得分:2)
我无法说出特定代码背后的开发人员的确切理由,但总的来说,我们使用最通用的可接受类型(通常是接口)进行输入,就像在视图中所做的那样。这使我们能够从不同的资源获取数据并在它们之间进行切换。
对于返回输出,除非函数根据某些条件返回不同的子类型,否则返回通用接口而不是实际类型没有任何好处,在您的示例中情况并非如此。或者,如果类本身正在实现接口,则出于上述相同原因,对于接口返回最通用的可接受类型很有用。这也不是您的情况,因此,在您的情况下,返回IEnumerable
没有任何好处。
答案 4 :(得分:1)
IEnumerable是接口,因此更加灵活,如果使用List,则将强制接收方法使用List。