2011-01-04 17 views
14

Tôi muốn có thể tạo biểu thức được biên dịch để đặt thuộc tính, với biểu thức lambda cung cấp phương thức "get" cho thuộc tính.Tạo Hành động <T> để "đặt" thuộc tính, khi tôi được cung cấp Biểu thức LINQ cho "get"

Dưới đây là những gì tôi đang tìm kiếm:

public Action<int> CreateSetter<T>(Expression<Func<T, int>> getter) 
{ 
    // returns a compiled action using the details of the getter expression tree, or null 
    // if the write property is not defined. 
} 

tôi vẫn đang cố gắng để hiểu được nhiều loại hình lớp biểu, vì vậy nếu bạn có thể chỉ cho tôi đi đúng hướng đó sẽ là tuyệt vời.

+1

Bạn không có nghĩa là 'Hành động '? Ngoài ra, chúng ta cũng có thể làm cho 'int' một tham số kiểu. – Ani

+0

Ani, vâng bạn nói đúng - Ý tôi là Action và tất nhiên int cũng là một tham số chung. – Alex

Trả lời

12

Sử dụng @ câu trả lời của Ani như là một điểm khởi đầu, bạn có thể sử dụng sau đây để tạo ra một biểu biên soạn.

[TestMethod] 
public void CreateSetterFromGetter() 
{ 
    Action<Person, int> ageSetter = InitializeSet((Person p) => p.Age); 
    Action<Person, string> nameSetter = InitializeSet((Person p) => p.Name); 

    Person p1 = new Person(); 
    ageSetter(p1, 29); 
    nameSetter(p1, "John"); 

    Assert.IsTrue(p1.Name == "John"); 
    Assert.IsTrue(p1.Age == 29); 
} 

public class Person { public int Age { get; set; } public string Name { get; set; } } 

public static Action<TContainer, TProperty> InitializeSet<TContainer, TProperty>(Expression<Func<TContainer, TProperty>> getter) 
{ 
    PropertyInfo propertyInfo = (getter.Body as MemberExpression).Member as PropertyInfo; 

    ParameterExpression instance = Expression.Parameter(typeof(TContainer), "instance"); 
    ParameterExpression parameter = Expression.Parameter(typeof(TProperty), "param"); 

    return Expression.Lambda<Action<TContainer, TProperty>>(
     Expression.Call(instance, propertyInfo.GetSetMethod(), parameter), 
     new ParameterExpression[] { instance, parameter }).Compile(); 
} 

Bạn nên lưu lại biểu thức đã biên dịch để giữ cho nó tiện dụng cho nhiều lần sử dụng.

+0

Hoàn toàn đúng, nhưng tôi không chắc chắn một biểu thức là tuyến đường tốt nhất ở đây. Nhưng ví dụ này là một ví dụ tốt (để minh hoạ). –

+0

+1 Đẹp, Adam. – Ani

+0

Tôi đánh dấu đây là câu trả lời vì nó sử dụng các lớp Expression như là giải pháp, đó là những gì tôi đang tìm kiếm. Cảm ơn Adam – Alex

7

Tất nhiên, bạn có thể đi bộ biểu thức-cây và sau đó sử dụng Delegate.CreateDelegate để tạo Action<,> thích hợp. Nó khá đơn giản, ngoại trừ tất cả các kiểm tra xác nhận hợp lệ (tôi không chắc chắn nếu tôi đã bao trả mọi thứ):

Tôi không có chuyên gia về biểu thức, nhưng tôi không nghĩ rằng xây dựng một biểu thức -tree và sau đó gọi Compile là có thể ở đây vì cây biểu thức không thể chứa các câu lệnh gán, theo như tôi biết. (EDIT: Rõ ràng, chúng đã được thêm vào trong .NET 4. Đó là một tính năng khó tìm thấy vì trình biên dịch C# dường như không thể xây dựng chúng từ lambdas).

public static Action<TContaining, TProperty> 
    CreateSetter<TContaining, TProperty> 
    (Expression<Func<TContaining, TProperty>> getter) 
{ 
    if (getter == null) 
     throw new ArgumentNullException("getter"); 

    var memberEx = getter.Body as MemberExpression; 

    if (memberEx == null) 
     throw new ArgumentException("Body is not a member-expression."); 

    var property = memberEx.Member as PropertyInfo; 

    if (property == null) 
     throw new ArgumentException("Member is not a property."); 

    if(!property.CanWrite) 
     throw new ArgumentException("Property is not writable."); 

    return (Action<TContaining, TProperty>) 
      Delegate.CreateDelegate(typeof(Action<TContaining, TProperty>), 
            property.GetSetMethod()); 
} 

Cách sử dụng:

public class Person { public int Age { get; set; } } 

... 

static void Main(string[] args) 
{ 
    var setter = CreateSetter((Person p) => p.Age); 
    var person = new Person(); 
    setter(person, 25); 

    Console.WriteLine(person.Age); // 25  
} 

Do lưu ý rằng điều này tạo ra một thể hiện ủy nhiệm mở, có nghĩa là nó không bị ràng buộc vào bất kỳ trường hợp cụ thể của TContaining. Thật đơn giản để sửa đổi nó để được ràng buộc với một cá thể cụ thể cụ thể; bạn cũng sẽ phải vượt qua một phương thức TContaining và sau đó sử dụng quá tải khác nhau của Delegate.CreateDelegate. Chữ ký của phương thức sẽ trông giống như sau:

public static Action<TProperty> CreateSetter<TContaining, TProperty> 
     (Expression<Func<TContaining, TProperty>> getter, TContaining obj) 
+2

Không chắc chắn, nhưng tôi nghĩ rằng họ đã thêm bài tập vào .net 4. Nhưng setter nên đã có chữ ký chính xác, vì vậy nó không cần thiết. – CodesInChaos

+2

Có, nhiệm vụ là 4.0 - nhưng bạn thực sự không cần nó ở đây; Delegate.CreateDelegate là lý tưởng. +1 –

+0

@CodeInChaos, @Marc Gravell: Cảm ơn, đó là tin cho tôi. – Ani

4

Chỉ trỏ Tôi sợ (tôi không có máy tính) - nhưng;

  • .Body của lambda rất có thể sẽ được MemberExpression
  • làm một dàn diễn viên an toàn (as vv) và truy cập vào .Member
  • kể từ khi bạn belive này một tài sản, điều này sẽ là một PropertyInfo, vì vậy thử nghiệm/diễn viên vv
  • từ một PropertyInfo, gọi GetSetMethod() để có được những MethodInfo tương ứng
  • sử dụng Delegate.CreateDelegate để có được điều đó như một đại biểu (đi qua các loại hành động)
  • thức ly, đúc các đại biểu trở lên dự kiến ​​ đại biểu loại
+1

Tâm trí tuyệt vời .... – Ani

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