2014-09-06 15 views
7

Dưới đây là một mã trình diễn đơn giản về vấn đề của tôi.Nhận ConstantExpression.Value khi giá trị thực tế được đưa vào DisplayClass do đóng

[TestClass] 
public class ExpressionTests 
{ 
    [TestMethod] 
    public void TestParam() 
    { 
     Search<Student>(s => s.Id == 1L); 

     GetStudent(1L); 
    } 

    private void GetStudent(long id) 
    { 
     Search<Student>(s => s.Id == id); 
    } 

    private void Search<T>(Expression<Func<T, bool>> filter) 
    { 
     var visitor = new MyExpressionVisitor(); 
     visitor.Visit(filter); 
    } 
} 

public class MyExpressionVisitor : ExpressionVisitor 
{ 
    protected override Expression VisitConstant(ConstantExpression node) 
    { 
     Assert.AreEqual(1L, node.Value); 
     return base.VisitConstant(node); 
    } 
} 

TestParam phương pháp gây VisitConstant được gọi trên hai con đường khác nhau:

1.TestParam ->Search ->VisitConstant

Trong con đường thực hiện này biểu hiện liên tục (1L) truyền cho Search là một giá trị hằng số thực. Ở đây, mọi thứ đều ổn, khẳng định thành công như mong đợi. Khi VisitConstant được gọi qua đường dẫn đầu tiên node.Value.GetType()Int64.Value1L.

2.TestParam ->GetStudent ->Search ->VisitConstant

Trong cụm từ này con đường thực hiện liên tục (id: 1L), được thực hiện bởi GetStudent như một tham số và truyền cho Search phương pháp bên trong một đóng cửa.

Vấn đề

Vấn đề là trên con đường thực hiện thứ hai. Khi VisitConstant được gọi qua đường dẫn thứ hai node.Value.GetType()MyProject.Tests.ExpressionTests+<>c__DisplayClass0 và lớp này có trường công khai có tên là id (giống như đối số của phương pháp GetStudent) có giá trị là 1L.

Câu hỏi

Làm thế nào tôi có thể nhận được id giá trị trong con đường thứ hai? Tôi biết về đóng cửa, những gì một DisplayClass là và tại sao nó được tạo ra tại thời gian biên dịch vv Tôi chỉ quan tâm đến việc nhận giá trị trường của nó. Một điều tôi có thể nghĩ đến là, qua sự phản chiếu. Với một cái gì đó như dưới đây nhưng nó không có vẻ gọn gàng.

node.Value.GetType().GetFields()[0].GetValue(node.Value); 

Bonus Vấn đề

Trong khi chơi với các mã cho gettting id giá trị tôi đã thay đổi VisitConstant phương pháp như dưới đây (mà sẽ không giải quyết vấn đề của tôi mặc dù) và nhận được một ngoại lệ nói " 'đối tượng' không không chứa một định nghĩa cho 'id'"

enter image description here

Câu hỏi thưởng

Khi động lực được giải quyết trong thời gian chạy và DisplayClass được tạo tại thời gian biên dịch, tại sao chúng ta không thể truy cập trường của nó bằng dynamic? Trong khi mã bên dưới hoạt động, tôi dự kiến ​​mã đó cũng sẽ hoạt động.

var st = new {Id = 1L}; 
object o = st; 
dynamic dy = o; 
Assert.AreEqual(1L, dy.Id); 

Trả lời

3

Here is an article that explains how to do it, và bao gồm mã nào đó.Về cơ bản, những gì bạn có thể làm là tạo một biểu thức đại diện cho biểu thức con đó, biên dịch nó thành một đại biểu và sau đó thực hiện ủy nhiệm đó. (Điều này cũng giải thích làm thế nào để xác định subexpressions có thể được đánh giá, nhưng tôi đoán bạn không quan tâm đến điều đó.)

Sử dụng mã từ bài viết, sửa đổi mã của bạn để những điều sau đây sẽ làm việc:

private void Search<T>(Expression<Func<T, bool>> filter) 
{ 
    new MyExpressionVisitor().Visit(Evaluator.PartialEval(filter)); 
} 

Khi động lực được giải quyết trong thời gian chạy và DisplayClass được tạo tại thời gian biên dịch, tại sao chúng tôi không thể truy cập các trường của nó bằng dynamic?

Vì rằng DisplayClass là một lớp private lồng vào bên trong ExpressionTests, do đó, mã bên MyExpressionVisitor không thể truy cập các thành viên của nó.

Nếu bạn thực hiện MyExpressionVisitor một lớp lồng nhau bên trong ExpressionTests, dynamic sẽ bắt đầu làm việc trên DisplayClass.

Các loại ẩn danh không hoạt động theo cách này, vì chúng không được phát ra dưới dạng lồng nhau private loại.

2

VisitConstant sẽ không giúp đỡ ở đây, vì nó nhận được một trình biên dịch được xây dựng ConstantExpression sử dụng đối tượng của lớp ẩn danh riêng để lưu trữ các giá trị lambda đã được đóng cửa trên (The DisplayClassxxx)

Thay vào đó, chúng ta nên ghi đè VisitMember phương pháp và kiểm tra MemberExpression của nó đã có ConstantExpression là một bên trong Expression.

Đây là thử nghiệm đang hoạt động với ít sự phản ánh.

[TestClass] 
public class UnitTest2 
{ 
    [TestMethod] 
    public void TestMethod2() 
    { 
     Search<Student>(s => s.Id == 1L); 
     GetStudent(1L); 
    } 
    private void GetStudent(long id) 
    { 
     Search<Student>(s => s.Id == id); 
    } 
    private void Search<T>(Expression<Func<T, bool>> filter) 
    { 
     var visitor = new MyExpressionVisitor2(); 
     visitor.Visit(filter.Body); 
    } 
} 

//ExpressionVisitor 
public class MyExpressionVisitor2 : ExpressionVisitor 
{ 
    protected override Expression VisitMember(MemberExpression node) 
    { 
     switch (node.Expression.NodeType) 
     { 
      case ExpressionType.Constant: 
      case ExpressionType.MemberAccess: 
      { 
       var cleanNode = GetMemberConstant(node); 

       //Test 
       Assert.AreEqual(1L, cleanNode.Value); 

       return cleanNode; 
      } 
      default: 
      { 
       return base.VisitMember(node); 
      } 
     } 
    } 


    private static ConstantExpression GetMemberConstant(MemberExpression node) 
    { 
     object value; 

     if (node.Member.MemberType == MemberTypes.Field) 
     { 
      value = GetFieldValue(node); 
     } 
     else if (node.Member.MemberType == MemberTypes.Property) 
     { 
      value = GetPropertyValue(node); 
     } 
     else 
     { 
      throw new NotSupportedException(); 
     } 

     return Expression.Constant(value, node.Type); 
    } 
    private static object GetFieldValue(MemberExpression node) 
    { 
     var fieldInfo = (FieldInfo)node.Member; 

     var instance = (node.Expression == null) ? null : TryEvaluate(node.Expression).Value; 

     return fieldInfo.GetValue(instance); 
    } 

    private static object GetPropertyValue(MemberExpression node) 
    { 
     var propertyInfo = (PropertyInfo)node.Member; 

     var instance = (node.Expression == null) ? null : TryEvaluate(node.Expression).Value; 

     return propertyInfo.GetValue(instance, null); 
    } 

    private static ConstantExpression TryEvaluate(Expression expression) 
    { 

     if (expression.NodeType == ExpressionType.Constant) 
     { 
      return (ConstantExpression)expression; 
     } 
     throw new NotSupportedException(); 

    } 
} 
+0

Tốt! Đây là cách nhanh nhất để có được các giá trị cơ bản của việc đóng, đây là câu trả lời được chấp nhận. – jorgebg

Các vấn đề liên quan