2010-05-27 2 views
1

설문 목록에서 평균값을 모두 계산해야합니다. 설문 조사에는 int 및 double 값을 갖는 많은 속성이 있습니다. 모든 계산을 처리 할 비즈니스 개체를 만들고 있는데 (100이 있습니다) 특정 속성의 평균을 찾기 위해 100 가지 방법을 코딩하지 않을 것입니다.속성 이름의 문자열 표현을 사용하여 LINQ C#의 평균값을 계산하십시오.

UI가 문자열 (속성을 나타내는)을 전달하고 해당 비즈니스 개체가 해당 속성에 대한 평균을 반환하도록하고 싶습니다.

그래서

같은 ...

INT AverageHeightInInches = MyObject.GetIntAverage ("HeightInInches을"); . . . 그런 다음 linq 코드를 사용하여 결과를 계산하십시오.

감사합니다.

+0

여기서 linq2sql은 무엇입니까? – luke

답변

4

, 그것은 속성 이름을 기준으로 평균을 계산할 수있는 기능을 만들 수 System.Linq.Expression 네임 스페이스를 사용합니다. 함수는 나중에 사용하기 위해 캐시 될 수 있으며, 리플렉션은 함수가 실행될 때가 아니라 함수를 생성하는 데에만 사용됩니다.

EDIT : 기존의 반사 예제를 제거하고 현재 예제를 업데이트하여 속성 목록을 처리하는 기능을 보여줍니다.

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; } 
} 
+0

나는 이것을 좋아하지만, 답이라고 말하기 전에 그것을 시험 할 필요가있다. 시험 할 수있을 때까지 내일까지 투표에 올랐다. – Paul

+0

문제 없습니다. 귀하의 요구에 맞게 적용 할 수 있기를 바랍니다. –

+0

다른 질문을해야하는지 잘 모르겠 음 좋아요 ... 작동하지만 확장 속성은 어떻습니까? 설문 조사가 다른 개체로 구성되어 있고 원하는 경우입니다. 하위 오브젝트의 속성 중 하나의 평균을 구하십시오. Survey.ExpenseInformation.ManHoursPerYear <- 즉 할 수 있도록하는 int 사랑의 INT AverageManHoursPerYear = MyObject.GetIntAverage ("ExpenseInformation.ManHoursPerYear"); – Paul

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(); 
     } 
+0

이것도 좋지만 내일이면 철저히 테스트해야합니다. 그래도 하나. – Paul

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); 
+0

실제로 무엇을 찾고 있었는지 ... 나는 알았지 만 객체의 인스턴스 (실제 속성)가 아닌 속성 이름의 문자열 represeentation 만 있습니다. 다른 대답은 자리에 있습니다. – Paul

+0

아, 가장 명백하게 보였으므로 문자열 이름을 사용하고 있다고 생각했습니다. 이 예제에서 하드 코딩 했으므로이 솔루션으로도 충분하다고 가정했습니다. –

1

당신이 linq2sql를 사용하는 경우 내가 DynamicLinq

을 제안

다음에 할 수 있습니다.

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

동적 linq 프로젝트는 문자열 오버로드를 IQueryable에 제공합니다.

관련 문제