代码之家  ›  专栏  ›  技术社区  ›  Paul

使用属性名称的字符串表示法计算Linq c中的平均值

  •  1
  • Paul  · 技术社区  · 14 年前

    我需要计算一系列调查的平均值。调查中有许多属性是int和双值的。我正在创建一个业务对象来处理所有的计算(大约有100个),我不希望用100种不同的方法来计算特定属性的平均值。

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

    所以,像…

    int averageheightininches=myObject.getIntaverage(“heightininches”); . . . 然后用LINQ代码计算结果。

    谢谢!

    4 回复  |  直到 14 年前
        1
  •  4
  •   Rohan West    14 年前

    我创建了这个小示例,它使用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; }
    }
    
        2
  •  1
  •   Darryl Braaten    14 年前

    下面是一个例子。

            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();
            }
    
        3
  •  1
  •   luke    14 年前

    如果您使用linq2sql,我建议 DynamicLinq

    你可以就这么做

       datacontext.Surveys.Average<double>("propertyName");
    

    动态Linq项目为iQuery提供字符串重载。

        4
  •  1
  •   Bryan Watts    14 年前

    你可以不经思考就这样做(两者都是 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);