返回类型为IEnumerable而不是List?

时间:2018-12-10 17:57:36

标签: c# asp.net-mvc ienumerable

我正在做一个.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返回类型?

5 个答案:

答案 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。