C# 是否将类型返回为IEnumerable而不仅仅是List?
我正在做一个.NETMVC教程。话虽如此,我还是遇到了这样的代码:C# 是否将类型返回为IEnumerable而不仅仅是List?,c#,asp.net-mvc,ienumerable,C#,Asp.net Mvc,Ienumerable,我正在做一个.NETMVC教程。话虽如此,我还是遇到了这样的代码: public class MoviesController : Controller { public ActionResult Index() { var movies = GetMovies(); return View(movies); } private IEnumerable<Movie> GetMovies() {
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>
所以我的问题是,为什么在私有方法GetMovies中的MoviesController中使用IEnumerable返回类型?为什么不使用列表返回类型?IEnumerable是接口,因此它更灵活,如果使用列表,则强制接收方法使用列表 IEnumerable是一个接口,所以它更灵活,如果使用List,则强制接收方法使用expect List IEnumerable是与遍历列表相关的接口。返回列表将立即向GetMovies的使用者公开比严格必要的更多的操作,例如从集合中添加或删除操作,这会更容易引入错误。在技术上没有理由选择IEnumerable而不是List,因为在引擎盖下它的行为是相同的。这个决定纯粹是实用的。IEnumerable是与迭代列表相关的接口。返回列表将立即向GetMovies的使用者公开比严格必要的更多的操作,例如从集合中添加或删除操作,这会更容易引入错误。在技术上没有理由选择IEnumerable而不是List,因为在引擎盖下它的行为是相同的。这个决定纯粹是实用的。我无法解释开发人员在特定代码背后的确切推理,但一般来说,我们使用最通用的可接受类型,通常是用于输入的接口,就像在视图中所做的那样。这允许我们从不同的资源中获取数据并在它们之间切换
至于返回输出,返回泛型接口而不是实际类型没有任何好处,除非函数根据某些条件返回不同的子类型,在您的示例中并非如此。或者,如果类本身正在实现一个接口,那么出于上述相同的原因,接口返回最通用的可接受类型是很有用的。这也不是您的情况,因此在您的情况下,返回IEnumerable没有任何好处。我无法解释开发人员在特定代码背后的确切推理,但一般来说,我们使用最通用的可接受类型,通常是一个输入接口,就像在视图中所做的那样。这允许我们从不同的资源中获取数据并在它们之间切换
至于返回输出,返回泛型接口而不是实际类型没有任何好处,除非函数根据某些条件返回不同的子类型,在您的示例中并非如此。或者,如果类本身正在实现一个接口,那么出于上述相同的原因,接口返回最通用的可接受类型是很有用的。这也不是您的情况,因此在您的情况下,返回IEnumerable没有任何好处。使用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"};
}
使用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"};
}
是否将类型返回为IEnumerable而不仅仅是List
可改为:
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();
}
现在我们不知道这些电影是如何取回的。。。只要合同/接口满足我们的数据需求,我们就不应该在意
是否将类型返回为IEnumerable而不仅仅是List
可改为:
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();
}
现在我们不知道这些电影是如何取回的。。。只要契约/接口满足我们的数据需求,我们就不必在意。这样,您可以将其更改为返回数组或任何实现IEnumerable的内容,而不必更改使用它的代码。基本上,您只需公开具有所需功能的最小派生类型,这样您就可以更改它以返回数组或实现IEnumerable的任何内容,而无需更改使用它的代码。基本上,您只需公开具有所有所需功能的最小派生类型。谢谢,我给您+,但Luke Samuel的答案更复杂,所以我将标记他的答案。这是错误的。您完全可以将列表返回到需要IEnumerable的视图中。谢谢@RacilHilan的说明。@RacilHilan:需要数组的方法如何?我不明白您的问题。怎么样?评论不是提问和回答问题的好地方。如果你想让我们给你一个好的答案,你可以发布一个带有代码的问题。或者至少解释一下你所说的期望数组的方法是什么意思。谢谢,我给你+,但Luke Samuel的答案更复杂,所以我会标记他的答案。这是错误的。您完全可以返回一个列表
对于需要IEnumerable的视图。谢谢@RacilHilan的澄清。@RacilHilan:需要数组的方法如何?我不明白你的问题。怎么样?评论不是提问和回答问题的好地方。如果你想让我们给你一个好的答案,你可以发布一个带有代码的问题。或者至少解释一下你所说的一个需要数组的方法是什么意思。这只适用于接收端,即视图,而不是给出端,即GetMovies函数。您可以将List从函数返回到需要IEnumerable的视图,并且仍然具有完全相同的解耦。更多细节见我的答案。我不同意;这与脱钩并不完全相同。通过在方法定义中公开列表,服务承诺提供一个列表,该列表可能非常重要,例如,如果该方法是接口或契约的一部分,并且存在向前兼容性的要求。有时明智的做法是只做你需要做的承诺,而不做更多。谢谢你的反馈。是的,这正是我在回答中解释的。但对于问题中的代码,这些条件都不适用。类没有实现接口,函数返回一个列表。在这种特定情况下,将接口用作返回类型绝对没有好处,并且它不会以任何方式改变耦合/解耦。对我来说,令人困惑的是您讨论与所讨论的代码相关的耦合的方式。耦合是一个扩展到所讨论的代码之外的概念,因为它与一件事情的更改是否需要另一件事情的更改有关。在我的回答中,我展示了六种方法来更改方法,如果您使用具体类型,那么也需要更改调用代码。所以说耦合没有区别是不正确的。仔细阅读问题。OP没有询问调用代码,即视图。类型在那里定义为IEnumerable,因此它将接受答案中的所有这些方法。OP询问了GetMovies函数返回的类型。我是说在问题中的特定代码中,将返回类型从IEnumerable更改为List不会以任何方式影响耦合。视图仍然接受IEnumerable,您的所有函数都将使用它。函数返回类型唯一起作用的是函数是否正在实现接口。这仅在接收端(即视图)正确,而在给出端(即GetMovies函数)正确。您可以将List从函数返回到需要IEnumerable的视图,并且仍然具有完全相同的解耦。更多细节见我的答案。我不同意;这与脱钩并不完全相同。通过在方法定义中公开列表,服务承诺提供一个列表,该列表可能非常重要,例如,如果该方法是接口或契约的一部分,并且存在向前兼容性的要求。有时明智的做法是只做你需要做的承诺,而不做更多。谢谢你的反馈。是的,这正是我在回答中解释的。但对于问题中的代码,这些条件都不适用。类没有实现接口,函数返回一个列表。在这种特定情况下,将接口用作返回类型绝对没有好处,并且它不会以任何方式改变耦合/解耦。对我来说,令人困惑的是您讨论与所讨论的代码相关的耦合的方式。耦合是一个扩展到所讨论的代码之外的概念,因为它与一件事情的更改是否需要另一件事情的更改有关。在我的回答中,我展示了六种方法来更改方法,如果您使用具体类型,那么也需要更改调用代码。所以说耦合没有区别是不正确的。仔细阅读问题。OP没有询问调用代码,即视图。类型在那里定义为IEnumerable,因此它将接受答案中的所有这些方法。OP询问了GetMovies函数返回的类型。我是说在问题中的特定代码中,将返回类型从IEnumerable更改为List不会以任何方式影响耦合。视图仍然接受IEnumerable,您的所有函数都将使用它。函数返回类型唯一起作用的是函数是否实现了一个接口。哇,谢谢Racil对我的问题的回答。你给了我一个很好的知识!顺便说一句,使用IEnumerable并不能加快应用程序的速度,是不是我在想这可能是原因?不是,即使返回类型是IEnumerable,代码仍然返回一个列表。唯一的区别是使用者只能看到IEnumerable公开的方法,直到它被转换到另一个类型。而不是返回它,
不。在视图中,通过IEnumerable循环的速度或快或慢可能有所不同,这取决于它是如何完成的。但是返回列表而不是IEnumerable不会以任何可测量的方式影响性能。哇,谢谢Racil对我问题的回答。你给了我一个很好的知识!顺便说一句,使用IEnumerable并不能加快应用程序的速度,是不是我在想这可能是原因?不是,即使返回类型是IEnumerable,代码仍然返回一个列表。唯一的区别是,消费者只能看到IEnumerable公开的方法,直到它被强制转换为另一种类型。不是为了返回它,不是。在视图中,通过IEnumerable的循环可能会更快或更慢,这取决于它是如何完成的。但是返回列表而不是IEnumerable不会以任何可测量的方式影响性能。哇,在这个答案上花费了很多精力。在上一个示例中,您注入了movieDb,但随后使用了iocDA。打字错误?这是很多信息@Erik,非常感谢你的努力!我以后一定会再次回到你的答案,把它作为知识的源泉。现在,你的回答&拉西尔的回答对这个问题做出了巨大的澄清!哇,在这个答案上花了很多精力。在上一个示例中,您注入了movieDb,但随后使用了iocDA。打字错误?这是很多信息@Erik,非常感谢你的努力!我以后一定会再次回到你的答案,把它作为知识的源泉。现在,你的回答&拉西尔的回答对这个问题做出了巨大的澄清!
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"}
};
}
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();
}