Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~

12,744 views

Published on

Hokuriku.NET C# 勉強会「C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~」
https://atnd.org/events/57085

で使用した資料に加筆

1. LINQ to Objects 復習
2. IQueryable<t>
3. 式木 (Expression Tree)
4. 式木メタ プログラミング
5. LINQ プロバイダー

Published in: Technology
  • Be the first to comment

C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~

  1. 1. https://atnd.org/events/57085
  2. 2. • • • http://blog.shos.info • •
  3. 3. • http://slidesha.re/1tA0Tit • http://1drv.ms/1zs3n78 3 ソースコード 参照
  4. 4.
  5. 5. 1. 2. 3. 4. 5.
  6. 6. • IEnumerable<int> sequence1 = new[] { 1, 1, 2, 3, 5, 8, 13, 21, 34 }; IEnumerable<int> sequence2 = sequence1.Where (x => x % 2 == 0); IEnumerable<int> sequence3 = sequence2.Select (x => x * x ); foreach (int item in sequence3) Console.WriteLine(item); ソースコード 参照
  7. 7.
  8. 8. • ソースコード 参照 IEnumerable<int> sequence1 = new[] { 1, 1, 2, 3, 5, 8, 13, 21, 34 }; IEnumerable<int> sequence2 = sequence1.Where (x => x % 2 == 0); IEnumerable<int> sequence3 = sequence2.Select (x => x * x ); foreach (int item in sequence3) Console.WriteLine(item); 実際にsequence3 から値が取り出さ れるまで、sequence1 から値は取り 出されず、Where やSelect に渡した デリゲートも実行されない
  9. 9. • var data = new EmployeeDataClassesDataContext(); data.Log = Console.Out; var sequence1 = data.Employee; var sequence2 = sequence1.Where ( ソースコード 参照 employee => employee.Name.Contains("田") ); var sequence3 = sequence2.Select ( employee => new { 番号= employee.Id, 名前= employee.Name }); foreach (var employee in sequence3) Console.WriteLine("{0}: {1}", employee.番号, employee.名前);
  10. 10. • SELECT [t0].[Id], [t0].[Name] FROM [dbo].[Employee] AS [t0] • SELECT [t0].[Id] AS [番号], [t0].[Name] AS [名前] FROM [dbo].[Employee] AS [t0] WHERE [t0].[Name] LIKE @p0 -- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [%田%]
  11. 11. • public static class Enumerable { public static class Queryable { public static IQueryable<T> Where<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate); } public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, int, bool> predicate); } •
  12. 12. • • •
  13. 13. • class Program { static void Main() { ソースコード Func<int, int, int> sequence1 = (x, y) => x + y; Func<int, int, int> sequence2 = (x, y) => { return x + y; }; Expression<Func<int, int, int>> expression1 = (x, y) => x + y; //Expression<Func<int, int, int>> expression2 = (x, y) => { return x + y; }; } } ブロックが含まれるラムダ式は式として扱えない (IQueryable<T> には使えない) 参照
  14. 14. • • var sequence4 = from employee in data.Employee where employee.Name.Contains("田") select new { 番号= employee.Id, 名前= employee.Name };
  15. 15. • •
  16. 16. • • http://msdn.microsoft.com/ja-jp/library/system.linq.iqueryable(v=vs.110).aspx public interface IQueryable : IEnumerable { Type ElementType { get; } Expression Expression { get; } IQueryProvider Provider { get; } } public interface IQueryable<T> : IEnumerable<T>, IQueryable {}
  17. 17. • class Foo : IQueryable { public Type ElementType { get { throw new NotImplementedException(); } } public Expression Expression { get { throw new NotImplementedException(); } } public IQueryProvider Provider { get { throw new NotImplementedException(); } } public IEnumerator GetEnumerator() { throw new NotImplementedException(); } } ソースコード 参照
  18. 18. • ソースコード 参照 Expression<Func<int, int, int>> expression = (x, y) => x + y; ((Expression)expression).Show();
  19. 19.
  20. 20.
  21. 21.
  22. 22.
  23. 23.
  24. 24. • • • • • Assembly Module Type ・Class ・Interface ・Value Type FieldInfo PropertyInfo EventInfo MethodInfo ConstructorInfo ParameterInfo
  25. 25. • • • •
  26. 26. • • •
  27. 27. • • • • • •
  28. 28. • • • •
  29. 29. • • • CodeDOM CodeDOMProvider ソースコード (C#、VB、JScript) アセンブリ GenerateCodeFromNamespace CompileAssemblyFromDom
  30. 30. • • • •
  31. 31. • namespace CodeDomHelloWorldDemo { using System; class Program { static void Main() { Console.WriteLine("Hello world!"); Console.ReadKey(); } } } ソースコード 参照
  32. 32. • • • • http://msdn.microsoft.com/ja-jp/library/f7dy01k1(v=vs.110).aspx
  33. 33. • •
  34. 34. Expression<Func<Employee, bool>> expression = employee => employee.Name.Contains("山"); Parameters Body Object Method Arguments Expression Member employee => employee.Name.Con tains("山") employee.Name Contains employee Name “山” employee employee.Name.Co ntains("山")
  35. 35. • 1. 2. + => x y (x, y)
  36. 36. • Expression の派生クラス一覧- 継承階層- Expression クラス- MSDN ライブラリ
  37. 37. パラメーターのx とy は、(x, y) 部分 とx + y 部分で使われているが、 それぞれ1 インスタンスずつにする
  38. 38. • static Func<int, int, int> AddByExpression() { // 生成したい式 // (int x, int y) => x + y // 引数x の式 var x = Expression.Parameter(type: typeof(int)); // 引数y の式 var y = Expression.Parameter(type: typeof(int)); // x + y の式 var add = Expression.Add (left: x, right: y); // (x, y) => x + y の式 var lambda = Expression.Lambda (add, x, y ); // ラムダ式をコンパイルしてデリゲートとして返す return (Func<int, int, int>)lambda.Compile(); } ソースコード 参照
  39. 39. • static Func<int, int, int> AddByExpression() { // 生成したい式 // (int x, int y) => x + y // 引数x の式 var x = Expression.Parameter(type: typeof(int)); // 引数y の式 var y = Expression.Parameter(type: typeof(int)); // x + y の式 var add = Expression.Add (left: x, right: y); // (x, y) => x + y の式 var lambda = Expression.Lambda (add, x, y ); // ラムダ式をコンパイルしてデリゲートとして返す return (Func<int, int, int>)lambda.Compile(); } ソースコード 参照
  40. 40. • 1. 2. 3. • 1. 2. 3. 4.
  41. 41.
  42. 42.
  43. 43. • // Expression (式) によるメソッド呼び出しメソッドの生成 static Func<T, TResult> CallByExpression<T, TResult>(string methodName) { // 生成したい式の例: // (T item) => item.methodName() // 引数item の式 var parameterExpression = Expression.Parameter(type: typeof(T), name: "item"); // item.methodName() の式 var callExpression = Expression.Call( instance: parameterExpression, method : typeof(T).GetMethod(methodName, Type.EmptyTypes) ); // item => item.methodName() の式 var lambda = Expression.Lambda(callExpression, parameterExpression); // ラムダ式をコンパイルしてデリゲートとして返す return (Func<T, TResult>)lambda.Compile(); } ソースコード 参照
  44. 44. • •
  45. 45. • 1. 2.
  46. 46. • 1. 2. 1. 2. 3. 1. 2. 3. 4. 1. 2. 3.
  47. 47.
  48. 48.
  49. 49. • • • • • http://blog.jhashimoto.net/entry/20120616/1339806360
  50. 50. • • http://msdn.microsoft.com/ja-jp/library/bb546158.aspx • • http://blogs.msdn.com/b/mattwar/archive/2008/11/18/linq-links.aspx • • http://weblogs.asp.net/mehfuzh/writing-custom-linq-provider
  51. 51. LINQプロバイダー (IQueryProvider) クエリーコンテキスト 式 クエリー (IQueryable) 式を解釈
  52. 52. • • • •
  53. 53. • ソースコード pubic class QueryProvider : IQueryProvider { public IQueryable<TCollection> CreateQuery<TCollection>(Expression expression) { return new QueryableData<TCollection>(this, expression); } IQueryable IQueryProvider.CreateQuery(Expression expression) { return null; } public TResult Execute<TResult>(Expression expression) { return default(TResult); } public object Execute(Expression expression) { // ここで式木を解釈して、IEnumerable を作って返す } } 参照
  54. 54. • • •
  55. 55. • static void Main() { IQueryable<int> query1 = new QueryableData<int>(new QueryProvider()); Console.WriteLine(query1.Expression); IQueryable<int> query2 = query1.Where(x => x % 2 == 0); Console.WriteLine(query2.Expression); IQueryable<int> query3 = query2.OrderBy(x => x); Console.WriteLine(query3.Expression); IQueryable<int> query4 = query3.Select(x => x * x); Console.WriteLine(query4.Expression); foreach (int item in query4) Console.WriteLine(item); } ソースコード 参照
  56. 56. • value(ProviderSample.QueryableData`1[System.Int32]) value(ProviderSample.QueryableData`1[System.Int32]).Where(x => ((x % 2) == 0)) value(ProviderSample.QueryableData`1[System.Int32]).Where(x => ((x % 2) == 0)).OrderBy(x => x) value(ProviderSample.QueryableData`1[System.Int32]).Where(x => ((x % 2) == 0)).OrderBy(x => x).Select(x => (x * x)) 1 1 2 3 5 8 13 21 34
  57. 57. • •
  58. 58. • public class MyExpressionVisitor : ExpressionVisitor { protected override Expression VisitBinary(BinaryExpression expression) { return base.VisitBinary(expression); } protected override Expression VisitConstant(ConstantExpression expression) { return base.VisitConstant(expression); } protected override Expression VisitMethodCall(MethodCallExpression expression) { return base.VisitMethodCall(expression); } protected override Expression VisitParameter(ParameterExpression expression) { return base.VisitParameter(expression); } …… 等々…… } ソースコード 参照
  59. 59. • ☆式(x, y) => x + y 二項演算((x + y)) - 右辺: x, 左辺: y, 型: System.Int32 引数(x) - 名前: x, 型: System.Int32 引数(y) - 名前: y, 型: System.Int32 引数(x) - 名前: x, 型: System.Int32 引数(y) - 名前: y, 型: System.Int32 ☆式text => text.Contains("福") メソッドコール(text.Contains("福")) - メソッド名: Contains, 型: System.Boolean 引数(text) - 名前: text, 型: System.String 定数("福") - 値: 福, 型: System.String 引数(text) - 名前: text, 型: System.String
  60. 60. • public class QueryableTimeline<TElement> : IOrderedQueryable<TElement> { public IQueryProvider Provider { get; private set; } public Expression Expression { get; private set; } public Type ElementType { get { return typeof(TElement); } } public QueryableTimeline() { Provider = new TimelineQueryProvider(); Expression = Expression.Constant(this); } …… 途中省略…… } ソースコード 参照
  61. 61.

×