חיפוש גנרי לפי Property

Sea Bass

New member
חיפוש גנרי לפי Property

שלום.
אשמח לעזרה שלכם לגבי משהו קטן שאני מנסה לעשות באמצעות LinqExpression.
Class A
{
public int Id {get;set}
public string Name {get;set }
}
Class Help
{
public void Test<P> (IEnumerable<P> list , Expression<Func<ClassA,P>> action)
{
var intList = new List<ClassA>(){new ClassA{Id = 2 , Name = "A" },new ClassA {Id = 3 , Name = "BPB" } };
== // intList.Where( list.Contains(actionSomewho) // ==
}
}
מה שאני מנסה לבצע הוא חיפוש בתוך ה Where לפי Property שאני מקבל במתודה.
למה שאני מנסה להגיע זה נניח שיש לי 10 פריטים ב List של IntList עם Id מ 1 עד 10.
מי שקורא לפונקציה יכול להחליט האם החפש לפי ה ID או לפי ה Name , וגם הוא מספק לי List של אותו Type. אם נניח הוא רוצה לחפש על פי שם אז הוא מעביר לי List<string> ועם ה Property.Name ועל פי זה אני אבצע את החיפוש.

אני לא מצלחי לשלב את זה.
אודה לעזרה שלכם.
 

Sea Bass

New member
לא. השתמשתי ב Reflection

אבל אני בטוח שישנה דרך יותר נכונה לעשות את זה, אם כן אשמח ללמוד.
intList.Where((p) =>
{
var name = ((MemberExpression)action.Body).Member.Name;
var value = p.GetType().GetProperty
(name).GetValue(p);
return (list.Contains((P)value));
});
הוצאתי את השם של ה Property אליו הוא מצביע ואז קיבלתי את הערך שלו וחיפשתי ברשימה.
מה שכן זה לא עובד על IQueryble משום מה כי אז ה Where מקבל Epression במקום Func.
 

nocgod

New member
תנסה לעשות עם expression trees

העיניין בקוד הזה הוא שאתה מקמפל קוד IL פעם אחת ומריץ אותו כמה פעמים שאתה רוצה. כמובן שאתה יכול להוסיף לו גם השוואה כדי לפלטר לצורך העניין.

using System;using System.Collections.Generic;
using System.Linq.Expressions;
namespace Linq{ class Program { class A { public int Id { get; set; } public string Name { get; set; } } static void Main(string[] args) { var intList = new List<A>() { new A{Id = 2 , Name = "A" }, new A {Id = 3 , Name = "BPB" } }; var p = Expression.Parameter(typeof(A)); var expr = Expression.Lambda(Expression.PropertyOrField(p, "Name"), p); var c_expr = expr.Compile(); intList.ForEach(x => Console.WriteLine(c_expr.DynamicInvoke(x))); } }}
 

nocgod

New member
קח דוגמא שיותר קרובה למה שאתה מחפש

עשיתי גם השוואות של זמני ריצה (שים לב שיש שם השוואה של references בstrings אבל מאחר וC# עושה caching של strings ואני לא עושה בשום מקום explicitly new ל string, אזי השוואה של references מספיקה לנו)
Using LINQ with known parameter (Base line): 120msUsing compiled expression: 163ms, compilation time: 1ms
Using reflection: 1938ms
using System;using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;using System.Linq.Expressions;namespace Linq{ public class Program { public class A { public int Id { get; set; } public string Name { get; set; } } public static Func<TEntity, bool> GetComparer<TEntity, TProperty>( Expression<Func<TEntity, TProperty>> selector, TProperty value) { var propertyRef = selector.Body; var parameter = selector.Parameters[0]; var constantRef = Expression.Constant(value); var comparer = Expression.Lambda<Func<TEntity, bool>> (Expression.Equal(propertyRef, constantRef), parameter) .Compile(); return comparer; } static void Main(string[] args) { var list = CreateData(); var sw = new Stopwatch(); var compsw = new Stopwatch(); int[] counts = new int[3]; sw.Start(); foreach (var a in list.Where(x => x.Name == "A")) { counts[0]++; } sw.Stop(); Console.WriteLine("Using LINQ with known parameter (Base line): {0}ms", sw.ElapsedMilliseconds); sw.Reset(); sw.Start(); compsw.Start(); var comp = GetComparer((A x) => x.Name, "A"); compsw.Stop(); foreach (var a in list.Where(comp)) { counts[1]++; } sw.Stop(); Console.WriteLine("Using compiled expression: {0}ms, compilation time: {1}ms", sw.ElapsedMilliseconds, compsw.ElapsedMilliseconds); sw.Reset(); sw.Start(); foreach (var a in list.Where(x => x.GetType().GetProperty("Name").GetValue(x) == "A")) { counts[2]++; } sw.Stop(); Console.WriteLine("Using reflection: {0}ms", sw.ElapsedMilliseconds); if (counts[0] != counts[1] || counts[0] != counts[2] || counts[1] != counts[2]) { Console.WriteLine("there was an error in test {0} {1} {2}", counts[0], counts[1], counts[2]); } else { Console.WriteLine("{0} {1} {2}", counts[0], counts[1], counts[2]); } } public static ICollection<A> CreateData() { var list = new List<A>(); var id = 1; var rand = new Random(); for (int i = 0; i < 10000000; i++) { list.Add(new A { Id = id++, Name = rand.Next(2) == 1 ? "A" : "B" }); } &nbs
 

nocgod

New member
הקוד :)

using System;using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;using System.Linq.Expressions;namespace Linq{ public class Program { public class A { public int Id { get; set; } public string Name { get; set; } } public static Func<TEntity, bool> GetComparer<TEntity, TProperty>( Expression<Func<TEntity, TProperty>> selector, TProperty value) { var propertyRef = selector.Body; var parameter = selector.Parameters[0]; var constantRef = Expression.Constant(value); var comparer = Expression.Lambda<Func<TEntity, bool>> (Expression.Equal(propertyRef, constantRef), parameter) .Compile(); return comparer; } static void Main(string[] args) { var list = CreateData(); var sw = new Stopwatch(); var compsw = new Stopwatch(); int[] counts = new int[3]; sw.Start(); foreach (var a in list.Where(x => x.Name == "A")) { counts[0]++; } sw.Stop(); Console.WriteLine("Using LINQ with known parameter (Base line): {0}ms", sw.ElapsedMilliseconds); sw.Reset(); sw.Start(); compsw.Start(); var comp = GetComparer((A x) => x.Name, "A"); compsw.Stop(); foreach (var a in list.Where(comp)) { counts[1]++; } sw.Stop(); Console.WriteLine("Using compiled expression: {0}ms, compilation time: {1}ms", sw.ElapsedMilliseconds, compsw.ElapsedMilliseconds); sw.Reset(); sw.Start(); foreach (var a in list.Where(x => x.GetType().GetProperty("Name").GetValue(x) == "A")) { counts[2]++; } sw.Stop(); Console.WriteLine("Using reflection: {0}ms", sw.ElapsedMilliseconds); if (counts[0] != counts[1] || counts[0] != counts[2] || counts[1] != counts[2]) { Console.WriteLine("there was an error in test {0} {1} {2}", counts[0], counts[1], counts[2]); } else { Console.WriteLine("{0} {1} {2}", counts[0], counts[1], counts[2]); } } public static ICollection<A> CreateData() { var list = new List<A>(); var id = 1; var rand = new Random(); for (int i = 0; i < 10000000; i++) { list.Add(new A { Id = id++, Name = rand.Next(2) == 1 ? "A" : "B" }); } return list; } }}
 

Royi Namir

New member
Professional job


 
מציע לשנות משהו

בכל איטרציה של הפתרון מבוסס ה reflection יש את הביטוי:
GetType().GetProperty("Name")
אבל אפשר לעשות caching קל ל ProperyInfo שמקבלים מהביטוי הנ"ל ולשים אותו מחוץ ללולאה. משהו כמו:
var propInfo = typeof(A).GetProperty("Name")
יכול להיות שזה ישפיע במשהו.
 

nocgod

New member
עשיתי - הנה התוצאות (רמז: לא השתנה הרבה)

sw.Start(); var propsw = new Stopwatch();
var prop = list.First().GetType().GetProperty("Name"); // this is not a safe code, just for testing
propsw.Stop(); foreach (var a in list.Where(x => prop.GetValue(x) == "A")) { counts[2]++; } sw.Stop(); Console.WriteLine("Using reflection: {0}ms, extracting property info {1}ms", sw.ElapsedMilliseconds, propsw.ElapsedMilliseconds);
זה התוצאות ריצה:
Using LINQ with known parameter (Base line): 185msUsing compiled expression: 222ms, compilation time: 1ms
Using reflection: 1446ms, extracting property info 0ms
שים לב שזמן הוצאת המידע על הprop הוא אפסי, מה שמשפיע הוא הוצאת הערך. מה שכן העובדה שאנחנו לא עושים את הוצאת המידע על הפונקציה (כלומר החיפוש) כל פעם מחדש ללא ספק מקטינה במעט את זמן הריצה.
 
למעלה