我在lst
中有一个类型为DataResponse
的对象集合,我想做的是总结该集合中int
和decimal
的所有属性,并将每个属性的结果分配给另一个对象DataContainerResponse
,该对象具有与正在总结的那些完全相同的属性名称(和类型)。
我可以通过手动输入每个属性并执行.Sum(s=>s.<propertyname>
来手动执行此操作。但那是90年代。下面是我徒劳的尝试。坦率地说,我以前从来没有给lambda表达式分配过var,我甚至不知道这是否可能.Sum(s=><var name>)
;
public DataAggragationResponse doAggregation(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataContainerResponse rd = new DataContainerResponse();
//If I do it manually typing each prop by hand.
rd.VIOL = lst.Sum(s => s.VIOL);
//Automation!!!
foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
{
rd.GetType().GetProperties().SetValue(lst.Sum(s => propertyInfo.Name[0]));
}
}
如果您想使用完全反射,您可以尝试以下操作。我没有优化代码,只是尽可能快地完成它。抱歉看起来很乱,我假设属性名在聚合结果类和你要聚合的单位类中是相同的。
class Program
{
static void Main(string[] args)
{
var list = new List<DataResponse>();
list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
Stopwatch watch = new Stopwatch();
watch.Start();
var response = DoAggregationReflection(list);
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
watch.Reset();
watch.Start();
var response2 = DoAggregation(list);
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
}
public static DataAggragationResponse DoAggregationReflection(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataAggragationResponse aggrResponse = new DataAggragationResponse();
var responseType = typeof(DataResponse);
var aggrResponseType = typeof(DataAggragationResponse);
foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
{
aggrResponseType.GetProperty(propertyInfo.Name).SetValue(aggrResponse, lst.Sum(x => (int)responseType.GetProperty(propertyInfo.Name).GetValue(x)));
}
return aggrResponse;
}
public static DataAggragationResponse DoAggregation(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataAggragationResponse aggrResponse = new DataAggragationResponse();
aggrResponse.Stuff = lst.Sum(x => x.Stuff);
aggrResponse.Stuff2 = lst.Sum(x => x.Stuff2);
return aggrResponse;
}
}
public class DataResponse
{
public int Stuff { get; set; }
public int Stuff2 { get; set; }
}
public class DataAggragationResponse
{
public int Stuff { get; set; }
public int Stuff2 { get; set; }
}
但是,作为一个建议,如果你想采用这种方法,最好能缓存你所做的所有反射调用,因为它们是昂贵的。90年代的方法仍然会在基准上胜出。就像上面的例子可以像下面这样对简单的StopWatch进行基准测试。
1.8193
0.4476
Press any key to continue . . .
第一个是DoAggregationReflection的执行时间,最后一个是DoAggregation的执行时间。你可以尽可能地优化反射,但我认为它仍然无法与基本的那个竞争。
有时候90年代的要好得多。;) 虽然你仍然会用LINQ来做实际的求和,所以这不是90年代了,因为LINQ是在2007年根据维基百科诞生的。
希望这能帮到你。我希望我保留了这个问题的SO链接,我从一段时间前拉了这个。很抱歉没有提到他/她的名字。
using System.Reflection;
public static Dictionary<string, string> GetPropertiesValue(object o)
{
Dictionary<string, string> PropertiesDictionaryToReturn = new Dictionary<string, string>();
foreach (MemberInfo itemMemberInfo in o.GetType().GetMembers())
{
if (itemMemberInfo.MemberType == MemberTypes.Property)
{
//object PropValue = GetPropertyValue(OPSOP, item.Name);
//string itemProperty = itemMemberInfo.Name;
//string itemPropertyValue = o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString();
//Console.WriteLine(itemProperty + " : " + itemPropertyValue);
PropertiesDictionaryToReturn.Add(itemMemberInfo.Name, o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString());
}
}
return PropertiesDictionaryToReturn;
}
这不是你所需要的,但是,我认为你可以改变它。
我宁愿采取不同的方法。我会动态构建和编译(一次)这样的东西:
Func<DataContainerResponse, DataResponse, DataContainerResponse> aggregateFunc =
(result, item) =>
{
result.Prop1 += item.Prop1;
result.Prop2 += item.Prop2;
...
result.PropN += item.PropN;
return result;
}
(如果你想知道为什么签名像上面那样,答案是——因为它可以直接用于下面的Aggregate
过载)
可以这样做:
static readonly Func<DataContainerResponse, DataResponse, DataContainerResponse>
AggregateFunc = BuildAggregateFunc();
static Func<DataContainerResponse, DataResponse, DataContainerResponse> BuildAggregateFunc()
{
var result = Expression.Parameter(typeof(DataContainerResponse), "result");
var item = Expression.Parameter(typeof(DataResponse), "item");
var propertyTypes = new HashSet<Type> { typeof(decimal), typeof(int) };
var statements = item.Type.GetProperties()
.Where(p => propertyTypes.Contains(p.PropertyType))
.Select(p => Expression.AddAssign(
Expression.Property(result, p.Name),
Expression.Property(item, p)));
var body = Expression.Block(statements
.Concat(new Expression[] { result }));
var lambda = Expression.Lambda<Func<DataContainerResponse, DataResponse, DataContainerResponse>>(
body, result, item);
return lambda.Compile();
}
,用法很简单:
public DataContainerResponse DoAggregation(List<DataResponse> source)
{
return source.Aggregate(new DataContainerResponse(), AggregateFunc);
}