2012-04-11 26 views
8

Tôi đang cố gắng để tạo ra một Đại biểu Generic ActionContravariance trong Expressions

delegate void ActionPredicate<in T1, in T2>(T1 t1, T2 t2); 

public static ActionPredicate<T,string> GetSetterAction<T>(string fieldName) 
    { 

     ParameterExpression targetExpr = Expression.Parameter(typeof(T), "Target"); 
     MemberExpression fieldExpr = Expression.Property(targetExpr, fieldName); 
     ParameterExpression valueExpr = Expression.Parameter(typeof(string), "value"); 

     MethodCallExpression convertExpr = Expression.Call(typeof(Convert), "ChangeType", null, valueExpr, Expression.Constant(fieldExpr.Type)); 

     UnaryExpression valueCast = Expression.Convert(convertExpr, fieldExpr.Type); 
     BinaryExpression assignExpr = Expression.Assign(fieldExpr, valueCast); 
     var result = Expression.Lambda<ActionPredicate<T, string>>(assignExpr, targetExpr, valueExpr); 
     return result.Compile(); 
    } 

và đây là người gọi tôi

ActionPredicate<busBase, string> act = DelegateGenerator.GetSetterAction<busPerson>("FirstName"); 

và đây là đối tượng kinh doanh

public abstract class busBase 
{ 

} 
public class busPerson : busBase 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public int Age { get; set; } 

    public string GetFullName() 
    { 
     return string.Format("{0} {1}", FirstName, LastName); 
    } 
} 

và đây là lỗi gì tôi nhận được trong quá trình biên soạn

Cannot implicitly convert type 'BusinessObjects.ActionPredicate<BusinessObjects.busPerson,string>' to 'BusinessObjects.ActionPredicate<BusinessObjects.busBase,string>'. An explicit conversion exists (are you missing a cast?)  

GetSetterAction tôi là trở lại ActionPerdicate nơi như ở đây T là busPerson và tôi đang cố gắng để lưu trữ nó trong ActionPredicate giữ trong tâm trí về Contravariance. Nhưng nó không thành công. tôi không biết làm thế nào để tiến xa hơn. Hãy giúp tôi..!

+0

Bạn đã thử chuyển đổi rõ ràng chưa? – McGarnagle

Trả lời

8

Generic contravariance không không cho phép bạn gán một đại biểu D<TDerived> đến một đại biểu D<TBase> vì lý do chứng minh dưới đây (sử dụng Action<T1> đây):

Action<string> m1 = MyMethod; //some method to call 
Action<object> m2 = m1; //compiler error - but pretend it's not. 
object obj = new object(); 

m2(obj); //runtime error - not type safe 

Như bạn có thể thấy, nếu chúng tôi được phép thực hiện nhiệm vụ này, sau đó chúng tôi sẽ phá vỡ loại an toàn vì chúng tôi có thể thử và gọi đại biểu m1 bằng cách chuyển và thể hiện của object và không phải string. Tuy nhiên, theo cách khác, tức là sao chép tham chiếu ủy nhiệm vào một loại có kiểu tham số có nguồn gốc nhiều hơn nguồn là tốt. MSDN has a more complete example of generic co/contra variance.

Vì vậy, bạn cần phải thay đổi khai báo act thành ActionPredicate<busPerson, string> act hoặc, nhiều khả năng, hãy cân nhắc viết phương thức GetSetterAction để luôn trả lại ActionPredicate<busBase, string>. Nếu bạn làm điều đó, bạn cũng nên thêm các loại chế

where T1 : busBase 

Để phương pháp này, và bạn cũng sẽ cần phải thay đổi cách diễn đạt của bạn được xây dựng, thay thế hai dòng đầu tiên như sau:

ParameterExpression targetExpr = Expression.Parameter(typeof(busBase), "Target"); 
//generate a strongly-typed downcast to the derived type from busBase and 
//use that as the type on which the property is to be written 
MemberExpression fieldExpr = Expression.Property(
    Expression.Convert(targetExpr, typeof(T1)), fieldName); 

Thêm ràng buộc chung là một liên lạc tốt đẹp để đảm bảo rằng downcast này sẽ luôn luôn hợp lệ cho bất kỳ T1.

Trên ghi chú hơi khác - điều gì đã xảy ra với đại diện Action<T1, T2>? Nó dường như làm chính xác điều tương tự như của bạn? :)

+0

Như bạn đã đề xuất, tôi đã cố gắng thay đổi GeetSetterAction để trả về Hành động nhưng tôi đã kết thúc với lỗi này "ParameterExpression of type 'BusinessObjects.busPerson' không thể được sử dụng cho tham số delegate của loại 'BusinessObjects.busBase'". Tôi nhận được lỗi này ở dòng này. "var result = Expression.Lambda > (assignExpr, targetExpr, valueExpr);" – kans

+0

@kans - ah yes - bạn sẽ phải xây dựng cây biểu thức hơi khác: downcast từ 'busBase' thành' T1' khi tạo 'fieldExpr'. Đã cập nhật câu trả lời của tôi. –

+0

Tuyệt vời. Cảm ơn rất nhiều. nó hoạt động tuyệt vời. Tôi đang tạo một Công cụ Quy tắc dựa trên Biểu thức. Bạn có thể ném một số suy nghĩ của bạn về điều này .. – kans

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