使用属性名称的字符串表示计算LINQ C#中的平均值

时间:2010-05-27 21:07:22

标签: c# linq

我需要在调查列表中计算一大堆平均值。这些调查有许多属于int和double值的属性。我正在创建一个业务对象来处理所有的计算(有100个),我宁愿不编写100种不同的方法来查找特定属性的平均值。

我希望能够让UI传递一个字符串(表示属性)并让业务对象返回该属性的平均值。

所以,就像......

int AverageHeightInInches = MyObject.GetIntAverage(“HeightInInches”); 。 。 。 然后使用linq代码来计算结果。

谢谢!

4 个答案:

答案 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)

您可以在不反思的情况下执行此操作(支持intdouble):

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提供字符串重载。