Reputation: 359
I am doing a .NET MVC tutorial. With that being said, I've came across code like this:
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"}
};
}
}
Index view for Movies looks like:
@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>
So my question is, why in the MoviesController
in the private method GetMovies()
, the IEnumerable
return type is used? Why not to use the List
return type?
Upvotes: 1
Views: 1064
Reputation: 54618
Return type as IEnumerable instead of just 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>
While using an IEnumerable<>
is more Open sOlid Principles, I rarely recommend passing any collection type interface/class to a view.
While in C# arrays/collections are First-Class Citizens the issue is that they are not extensible while maintaining the Single Responsibility Principle.
For example:
// Controller Returns:
var people = .... as IEnumerable<Person>;
return View(people);
@model IEnumerable<Person>
Now suppose to want to add any information to the view that has nothing to do with the group (like a title to the page).. how do you do that? You could extend and make your own class that derives from IEnumerable<T>
but that breaks the SRP because the Title of the page has nothing to do with the group of people. Instead you should create a First-Class Model that represents everything the view needs:
public class MyViewModel
{
public string Title { get; set;}
public IEnumerable<Person> People { get; set;}
}
return View(myViewModel);
@model MyViewModel
I suggest always doing this. As soon as you start using partials or templates in MVC, or want to post back the same object, it becomes increasingly difficult to move away from IEnumerable<>
because you need to changes Partials and/or Templates and/or Javascript...
So my question is, why in the
MoviesController
in the private methodGetMovies()
, theIEnumerable<>
return type is used?
Generally it's good practice to Program against an Interface and not an Implementation, also referred to as Design by contract (DbC), also known as contract programming, programming by contract and design-by-contract programming,.
It's a soLid Principle specifically Liskov substitution principle. Excerpt:
Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.). More formally, the Liskov substitution principle (LSP) is a particular definition of a subtyping relation, called (strong) behavioral subtyping, that was initially introduced by Barbara Liskov in a 1987 conference keynote address titled Data abstraction and hierarchy. It is a semantic rather than merely syntactic relation, because it intends to guarantee semantic interoperability of types in a hierarchy, object types in particular. Barbara Liskov and Jeannette Wing described the principle succinctly in a 1994 paper as follows...
In practice it means the current code:
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"}
};
}
Could change to:
public class MoviesController : Controller
{
private readonly IMovieDb _movieDb;
// Dependency Injecting access to movies
public MoviesController(IMovieDb movieDb)
{
_movieDb = movieDb;
}
public ActionResult Index()
{
var movies = _movieDb .GetMovies();
return View(movies);
}
// ....
public interface IMovieDb
{
IEnumerable<Movie> GetMovies();
}
Now we have no idea how the Movies are retrieved... and we shouldn't care as long as the contract/interface fulfills our data needs.
Upvotes: 3
Reputation: 52210
Using IEnumerable weakens the coupling, which allows it to be forward compatible with other concrete types should the implementation change. All of the following changes could be made without changing the interface:
//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"};
}
Upvotes: 3
Reputation: 25341
I cannot speak to the exact reasoning of the developer behind a specific code, but in general we use the most generic acceptable type (usually an interface) for input, like done in the view. This allows us to get data from different resources and switch between them.
As for returning output, there is no benefit in returning the generic interface instead of the actual type unless the function returns different subtypes depending on some conditions, which is not the case in your example. Alternatively, if the class itself is implementing an interface then it is useful for the interface to return the most generic acceptable type for the same reason above. This is also not your case, so in your case, returning the IEnumerable
has no benefit.
Upvotes: 2
Reputation: 453
IEnumerable<>
is the interface relevant to iterating over a List<>
. Returning a List<>
would immediately expose more operations to the consumer of GetMovies()
than is strictly necessary--such as adding or removing from the collection--which could make it easier to introduce errors. There is no technical reason to choose IEnumerable<>
over List<>
, because under the hood it will behave the same. The decision is purely practical.
Upvotes: 3
Reputation: 766
IEnumerable is interface so it is more flexible , if you use List , you are forcing the receiving method to expect List.
Upvotes: 1