C# 选择许多匿名类型并跳过迭代
很长一段时间以来,我一直试图找到一种“干净”的模式来处理C# 选择许多匿名类型并跳过迭代,c#,anonymous-types,linq,C#,Anonymous Types,Linq,很长一段时间以来,我一直试图找到一种“干净”的模式来处理。当您不想总是返回结果时,请使用匿名类型选择many。我最常见的用例如下所示: //c is a customer var context = GetContextForCustomer(c); // look up some data, myData using the context connection if (someCondition) return myData.Select(x => new { CustomerID
。当您不想总是返回结果时,请使用匿名类型选择many
。我最常见的用例如下所示:
//c is a customer
var context = GetContextForCustomer(c);
// look up some data, myData using the context connection
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return null;
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
continue? return null? return empty list?
})
.ToList();
。选择many
//c is a customer
var context = GetContextForCustomer(c);
// look up some data, myData using the context connection
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return null;
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
continue? return null? return empty list?
})
.ToList();
这可以作为foreach语句实现:
var results = new List<WhatType?>();
foreach (var c in customers) {
var context = GetContextForCustomer(c);
if (someCondition)
results.AddRange(myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 }));
}
这两种方法都存在问题。foreach
解决方案需要初始化列表来存储结果,并且您必须定义类型。使用.SelectMany
和.Where
通常是不切实际的,因为someCondition
的逻辑相当复杂,并且依赖于某些数据查找。因此,我的理想解决方案如下所示:
//c is a customer
var context = GetContextForCustomer(c);
// look up some data, myData using the context connection
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return null;
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
continue? return null? return empty list?
})
.ToList();
要跳过返回值,我应该在else
行中输入什么?没有一个解决方案是我能想出的,或者是理想的:
continue
未编译,因为它不是活动的foreach
循环
返回null
导致NRE
return
空列表要求我再次初始化匿名类型的列表
有没有一种干净、简单、整洁的方法可以满足我所有的(挑剔的)要求?您可以尝试以下方法:
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return Enumerable.Empty<int>().Select(x => new { CustomerID = 0, X1 = "defValue", X2 = "defValue" });
})
.ToList();
客户
.天冬酰胺()
.SelectMany(c=>{
var context=GetContextForCustomer(c);
如果(某些条件)
返回myData.Select(x=>new{CustomerID=c,X1=x.X1,X2=x.X2});
其他的
返回Enumerable.Empty().Select(x=>new{CustomerID=0,X1=“defValue”,X2=“defValue”});
})
.ToList();
编译器将具有相同属性集(相同名称和类型)的所有匿名类型合并到一个匿名类中。这就是为什么您的选择
和可枚举上的一个都会返回相同的T
您可以返回一个空的可枚举
。下面是一个示例(虽然没有您的客户和someCondition
,因为我不知道他们是什么,但与您的示例具有相同的一般形式):
不知道某个条件和我的数据看起来像什么
为什么不选择和其中的上下文:
customers
.Select(c => GetContextForCustomer(c))
.Where(ctx => someCondition)
.SelectMany(ctx =>
myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
编辑:我刚刚意识到您需要进一步携带客户
和上下文
,因此您可以这样做:
customers
.Select(c => new { Customer = c, Context = GetContextForCustomer(c) })
.Where(x => someCondition(x.Context))
.SelectMany(x =>
myData.Select(d => new { CustomerID = x.Customer, X1 = d.x1, X2 = d.x2 });
您可以创建自己的SelectMany
LINQ方法变量,该方法支持null
s:
public static class EnumerableExtensions
{
public static IEnumerable<TResult> NullableSelectMany<TSource, TResult> (
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
foreach (TSource item in source) {
IEnumerable<TResult> results = selector(item);
if (results != null) {
foreach (TResult result in results)
yield return result;
}
}
}
}
公共静态类EnumerableExtensions
{
公共静态IEnumerable NullableSelectMany(
这是一个数不清的来源,
Func选择器)
{
if(source==null)
抛出新的ArgumentNullException(“源”);
if(选择器==null)
抛出新的ArgumentNullException(“选择器”);
foreach(源中的TSource项){
IEnumerable结果=选择器(项目);
如果(结果!=null){
foreach(TResult result in results)
收益结果;
}
}
}
}
现在,您可以在选择器lambda中返回null
。接受的答案返回动态
。最干净的方法是将过滤逻辑移动到中,其中
,这使得整个事情在linq上下文中看起来更好。既然你在问题中明确排除了这一点,而且我不喜欢在linq通话中用多行文字写代表,我会尝试,但有人会说它更粗糙
var results = new
{
customerID = default(int), //notice the casing of property names
x1 = default(U), //whatever types they are
x2 = default(V)
}.GetEmptyListOfThisType();
foreach (var customerID in customers) {
var context = GetContextForCustomer(customerID);
if (someCondition)
results.AddRange(myData.Select(x => new { customerID, x.x1, x.x2 }));
}
public static List<T> GetEmptyListOfThisType<T>(this T item)
{
return new List<T>();
}
var结果=新
{
customerID=default(int),//注意属性名的大小写
x1=默认值(U),//无论它们是什么类型
x2=默认值(V)
}.getEmptyListoftHistType();
foreach(客户中的var客户ID){
var context=GetContextForCustomer(customerID);
如果(某些条件)
AddRange(myData.Select(x=>new{customerID,x.x1,x.x2}));
}
公共静态列表GetEmptyListOftHistType(此T项)
{
返回新列表();
}
请注意,属性名称的正确使用与其他变量名称一致,因此您不必在Select
调用中再次写入属性名称。这将导致大量动态调用,进而导致反射。就性能而言,这不是最好的选择。很好。但是他也说他只需要这个来生成一些快速报告。我最喜欢这个解决方案,因为它很短,不需要任何定制的扩展方法。我尝试了Enumerable.Empty()
,但在没有
的情况下,它无法确定类型。谢谢如果您这样做,您会意识到最终的返回类型也将是IEnumerable
。根据您的用例,这可能会很好地工作。这是一种很好的方法。我考虑的另一件事是。选择(…)。其中(x=>x!=null)。选择多个(x=>x)
。没有那么漂亮,但不需要我在任何地方指定匿名类型。@mellamokb ReSharper实际上向我建议了这一点。:)对于IEnumerable
,我也有很多扩展方法,所以我会自己编写类似.Select(…).WhereNotNull().flatte()
,这对我来说已经足够了。我意识到我的示例做得不够好。实际上,someCondition
和myData
背后的逻辑是20-30行代码,并且发展迅速,因此我最喜欢中的所有代码。选择many
。这个解决方案是基本的