我需要在调查列表中计算一大堆平均值。这些调查有许多属于int和double值的属性。我正在创建一个业务对象来处理所有的计算(有100个),我宁愿不编写100种不同的方法来查找特定属性的平均值。
我希望能够让UI传递一个字符串(表示属性)并让业务对象返回该属性的平均值。
所以,就像......
int AverageHeightInInches = MyObject.GetIntAverage(“HeightInInches”); 。 。 。 然后使用linq代码来计算结果。
谢谢!
答案 0 :(得分:4)
我创建了这个小例子,它使用System.Linq.Expression命名空间创建一个可以根据属性名称计算平均值的函数。该函数可以缓存供以后使用,反射仅用于创建函数,而不是每次执行函数。
编辑:我删除了现有的反射示例并更新了当前示例,以显示遍历属性列表的功能。
static class Program
{
static void Main()
{
var people = new List<Person>();
for (var i = 0; i < 1000000; i++)
{
var person = new Person { Age = i };
person.Details.Height = i;
person.Details.Name = i.ToString();
people.Add(person);
}
var averageAgeFunction = CreateIntegerAverageFunction<Person>("Age");
var averageHeightFunction = CreateIntegerAverageFunction<Person>("Details.Height");
var averageNameLengthFunction = CreateIntegerAverageFunction<Person>("Details.Name.Length");
Console.WriteLine(averageAgeFunction(people));
Console.WriteLine(averageHeightFunction(people));
Console.WriteLine(averageNameLengthFunction(people));
}
public static Func<IEnumerable<T>, double> CreateIntegerAverageFunction<T>(string property)
{
var type = typeof(T);
var properties = property.Split('.'); // Split the properties
ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
Expression expression = parameterExpression;
// Iterrate over the properties creating an expression that will get the property value
for (int i = 0; i < properties.Length; i++)
{
var propertyInfo = type.GetProperty(properties[i]);
expression = Expression.Property(expression, propertyInfo); // Use the result from the previous expression as the instance to get the next property from
type = propertyInfo.PropertyType;
}
// Ensure that the last property in the sequence is an integer
if (type.Equals(typeof(int)))
{
var func = Expression.Lambda<Func<T, int>>(expression, parameterExpression).Compile();
return c => c.Average(func);
}
throw new Exception();
}
}
public class Person
{
private readonly Detials _details = new Detials();
public int Age { get; set; }
public Detials Details { get { return _details; } }
}
public class Detials
{
public int Height { get; set; }
public string Name { get; set; }
}
答案 1 :(得分:1)
这是一个例子。
class Survey
{
public int P1 { get; set; }
}
class MyObject
{
readonly List<Survey> _listofSurveys = new List<Survey> { new Survey { P1 = 10 }, new Survey { P1 = 20 } };
public int GetIntAverage(string propertyName)
{
var type = typeof(Survey);
var property = type.GetProperty(propertyName);
return (int)_listofSurveys.Select(x => (int) property.GetValue(x,null)).Average();
}
}
static void Main(string[] args)
{
var myObject = new MyObject();
Console.WriteLine(myObject.GetIntAverage("P1"));
Console.ReadKey();
}
答案 2 :(得分:1)
您可以在不反思的情况下执行此操作(支持int
和double
):
public static double Average(this IEnumerable<Survey> surveys, Func<Survey, int> selector)
{
return surveys.Average(selector);
}
public static double Average(this IEnumerable<Survey> surveys, Func<Survey, double> selector)
{
return surveys.Average(selector);
}
用法:
var average1 = surveys.Average(survey => survey.Property1);
var average2 = surveys.Average(survey => survey.Property2);
答案 3 :(得分:1)
如果您使用的是linq2sql,我会建议DynamicLinq
然后你就可以做到
datacontext.Surveys.Average<double>("propertyName");
动态linq项目为IQueryable提供字符串重载。