C# 来自LINQ Select的复杂IEnumerable结果
我试图理解LINQ select操作的结果类型。我很难理解如何利用结果 考虑以下几点:C# 来自LINQ Select的复杂IEnumerable结果,c#,linq,asp.net-core-mvc,C#,Linq,Asp.net Core Mvc,我试图理解LINQ select操作的结果类型。我很难理解如何利用结果 考虑以下几点: public class ReportContract { public int ContractId { get; set; } public string ContractName { get; set; } public string Title { get; set; } public string[] Details { get; set; } } public c
public class ReportContract
{
public int ContractId { get; set; }
public string ContractName { get; set; }
public string Title { get; set; }
public string[] Details { get; set; }
}
public class ReportContractVM
{
public int ContactId { get; set; }
public string ContractName { get; set; }
}
public class SomeController : ControllerBase
{
public ActionResult<IEnumerable<ReportContractVM>> Get()
{
IEnumerable<ReportContract> contracts = CreateConrtacts();
IEnumerable<ReportContractVM> result = contracts.Select(
x => new ReportContractVM
{
ContactId = x.ContractId,
ContractName = x.ContractName
}
);
return Ok(result);
}
private IEnumerable<ReportContract> CreateContracts()
{
List<ReportContract> contracts = new List<ReportContract>()
{
new ReportContract {
ContractId = 1234,
ContractName= "ABCD",
Details= new string[] {
"test", "Best", "rest", "jest"},
Title="First"
},
new ReportContract {
ContractId = 1235,
ContractName= "EFGH",
Details= new string[] {
"fish", "dish", "wish", "pish"},
Title="Second"
},
new ReportContract {
ContractId = 1236,
ContractName= "IJKL",
Details= new string[] {
"hot", "tot", "mot", "jot"},
Title="Third"
},
new ReportContract {
ContractId = 1237,
ContractName= "MNOP",
Details= new string[] {
"ring", "sing", "bing", "ping"},
Title="Fourth"
}
};
return contracts;
}
}
在调试器中检查分配给“result”的类型会产生:
System.Linq.Enumberable.SelectListIterator
<
ComplexIEnumerable.ReportContract,
ComplexIEnumerable.ReportContractVM
>
我不知道这与什么有关
IEnumerable<ReportContractVM>.
怎么样
IEnumerable<ReportContractVM>
是否从此结果中访问
请忽略本文的其余部分。聪明的编辑要求更多的细节,我认为这就足够了 我建议阅读这篇文章,解释什么是枚举器,又称收益返回方法物化——因为它是Linq如何工作以及如何使用IEnumerable结果的核心。请注意,IEnumerable既可以指物化集合,如T[]或列表,也可以指非物化枚举器: 至于你的情况,一般来说,切勿从ASP.NET Web API控制器返回非物化实体框架,即数据库支持的IEnumerable或IQueryable,或将非物化IEnumerable作为ViewModel的成员传递。这是因为父DbContext的生存期可能短于IEnumerable/IQueryable对象的生存期。但是,仅使用内存中的对象返回非物化集合是可以的,例如使用枚举数覆盖枚举值以获取SelectListItem对象: 具体来说,改变这一点:
IEnumerable<ReportContractVM> result = contracts.Select(
x => new ReportContractVM
{
ContactId = x.ContractId,
ContractName = x.ContractName
}
);
为此:
List<ReportContractVM> list = contracts
.Select( x => new ReportContractVM { ContractId = x.ContractId, ContractName = x.ContractName } )
.ToList();
另外,将ActionResult更改为使用IReadOnlyList而不是IEnumerable,这将有助于确保始终返回物化列表,而不是可能的非物化枚举数:
public class SomeController : ControllerBase
{
public ActionResult<IReadOnlyList<ReportContractVM>> Get()
{
}
}
顺便说一句,我在ActionResult与泛型和异步任务操作的配合方面一直存在问题,因此我个人更愿意这样做,但YMMV:
public class SomeController : ControllerBase
{
[Produces( typeof( IReadOnlyList<ReportContractVM> ) )
public IActionResult Get()
{
}
// if async:
[Produces( typeof( IReadOnlyList<ReportContractVM> ) )
public async Task<IActionResult> Get()
{
}
}
我建议阅读这篇文章,解释什么是枚举器(也称为yield-return-methods-materialization)——因为它是Linq如何工作以及如何使用IEnumerable结果的核心。请注意,IEnumerable既可以指物化集合,如T[]或List,也可以指非物化枚举器: 至于你的情况,一般来说,切勿从ASP.NET Web API控制器返回非物化实体框架,即数据库支持的IEnumerable或IQueryable,或将非物化IEnumerable作为ViewModel的成员传递。这是因为父DbContext的生存期可能短于IEnumerable/IQueryable对象的生存期。但是,仅使用内存中的对象返回非物化集合是可以的,例如使用枚举数覆盖枚举值以获取SelectListItem对象: 具体来说,改变这一点:
IEnumerable<ReportContractVM> result = contracts.Select(
x => new ReportContractVM
{
ContactId = x.ContractId,
ContractName = x.ContractName
}
);
为此:
List<ReportContractVM> list = contracts
.Select( x => new ReportContractVM { ContractId = x.ContractId, ContractName = x.ContractName } )
.ToList();
另外,将ActionResult更改为使用IReadOnlyList而不是IEnumerable,这将有助于确保始终返回物化列表,而不是可能的非物化枚举数:
public class SomeController : ControllerBase
{
public ActionResult<IReadOnlyList<ReportContractVM>> Get()
{
}
}
顺便说一句,我在ActionResult与泛型和异步任务操作的配合方面一直存在问题,因此我个人更愿意这样做,但YMMV:
public class SomeController : ControllerBase
{
[Produces( typeof( IReadOnlyList<ReportContractVM> ) )
public IActionResult Get()
{
}
// if async:
[Produces( typeof( IReadOnlyList<ReportContractVM> ) )
public async Task<IActionResult> Get()
{
}
}
基本上,输出是Select返回的具体可枚举类型,类型参数同时引用输入和输出类型。在下面的简化示例中可能更容易看到
namespace ExampleNamespace
{
class A
{
public A(int i) { }
}
}
现在假设我在某个地方有这个代码:
List<int> i = new List<int>() { 1, 2, 3, 4, 5, 6 };
var temp = i.Select(x => new A(x));
经检查,温度类型为:
System.Linq.Enumerable.WhereSelectListIterator<int, ExampleNamespace.A>
因此,要分解这一点:
System.Linq.Enumerable.WhereSelectListIterator是从Select返回的迭代器类型。这与您的类型略有不同,可能是由于.NET版本不同
int是起始IEnumerable保持的类型
ExampleMespace.A是输出类型的全名
话虽如此,你应该阅读并理解戴相龙在回答中的内容 基本上,输出是Select返回的具体可枚举类型,类型参数同时引用输入和输出类型。在下面的简化示例中可能更容易看到
namespace ExampleNamespace
{
class A
{
public A(int i) { }
}
}
现在假设我在某个地方有这个代码:
List<int> i = new List<int>() { 1, 2, 3, 4, 5, 6 };
var temp = i.Select(x => new A(x));
经检查,温度类型为:
System.Linq.Enumerable.WhereSelectListIterator<int, ExampleNamespace.A>
因此,要分解这一点:
System.Linq.Enumerable.WhereSelectListIterator是从Select返回的迭代器类型。这与您的类型略有不同,可能是由于.NET版本不同
int是起始IEnumerable保持的类型
ExampleMespace.A是输出类型的全名
话虽如此,你应该阅读并理解戴相龙在回答中的内容 在您通过foreach或类似.ToList的方法迭代结果之前,LINQ IEnumerable查询的结果是它内部使用的某种神奇的LINQ类型。如您所见,由于它没有发出错误信号,编译器认为该类型实现了您所期望的类型。如果在结果上调用.ToList,您将得到正确的列表。在您通过foreach或类似.ToList的方式迭代结果之前,LINQ IEnumerable查询的结果是它内部使用的某种神奇的LINQ类型。正如您所看到的,由于它没有发出错误信号,编译器认为
类型实现您期望的类型。如果你打电话告诉我结果,你会得到正确的名单。谢谢你的回复。根据您的建议,我发现这篇相关文章也很有帮助:。谢谢您的回复。根据您的建议,我发现这篇相关文章也很有帮助:。