2009-09-23 11 views
6

Điều này có thể áp dụng cho các địa điểm khác, nhưng trong WinForms, khi tôi sử dụng liên kết, tôi tìm thấy nhiều phương pháp muốn lấy tên của thuộc tính để liên kết. Một cái gì đó như:Có cách nào làm cho C# ràng buộc hoạt động tĩnh không?

class Person 
{ 
    public String Name { get { ... } set { ... } } 
    public int Age { get { ... } set { ... } } 
} 

class PersonView 
{ 
    void Bind(Person p) 
    { 
     nameControl.Bind(p,"Name"); 
     ageControl.Bind(p,"Age"); 
    } 
} 

Vấn đề lớn mà tôi tiếp tục với điều này là "Tên" và "Độ tuổi" được chỉ định là chuỗi. Điều này có nghĩa là trình biên dịch không giúp được gì nếu ai đó đổi tên một thuộc tính của Person. Mã sẽ biên dịch tốt, nhưng các ràng buộc sẽ bị phá vỡ.

Có cách nào tiêu chuẩn để giải quyết vấn đề này mà tôi đã bỏ lỡ không? Nó cảm thấy như tôi cần một số từ khóa, có thể được gọi là stringof để phù hợp với typeof hiện có. Bạn có thể sử dụng nó như sau:

ageControl.Bind(p,stringof(p.Age).Name); 

stringof có thể trả về một số lớp có thuộc tính để lấy toàn bộ đường dẫn, hoặc chuỗi để bạn có thể phân tích nó.

Có phải thứ như thế này đã có thể thực hiện được không?

+1

Đây không phải là C# mà gắn bó với động rằng có rất nhiều, điều này là WinForms. –

+0

Đúng. Loại tính năng ngôn ngữ này có thể có ích mặc dù bạn không sử dụng WinForms. –

Trả lời

2

Bạn có thể sử dụng biểu thức để có được bindings kiểm tra biên dịch. Ví dụ, trong một trong những dự án hiện tại chúng tôi thiết lập các ràng buộc như thế này:

DataBinder 
    .BindToObject(this) 
    .ObjectProperty(c => c.IsReadOnly) 
     .Control(nameTextBox, n => n.ReadOnly) 
     .Control(addressControl, n => n.ReadOnly) 

Mã hỗ trợ kiểu này được tách ra thành nhiều lớp:

public static class DataBinder 
{ 
    public static DataBinderBindingSourceContext<TDataSource> BindToObject<TDataSource>(TDataSource dataSource) 
    { 
     return new DataBinderBindingSourceContext<TDataSource>(dataSource); 
    } 
} 

public class DataBinderBindingSourceContext<TDataSource> 
{ 
    public readonly object DataSource; 

    public DataBinderBindingSourceContext(object dataSource) 
    { 
     DataSource = dataSource; 
    } 

    public DataBinderControlContext<TDataSource, TProperty> ObjectProperty<TProperty>(Expression<Func<TDataSource, TProperty>> property) 
    { 
     return new DataBinderControlContext<TDataSource, TProperty>(this, property); 
    } 
} 

public class DataBinderControlContext<TDataSource, TProperty> 
{ 
    readonly DataBinderBindingSourceContext<TDataSource> BindingSourceContext; 
    readonly string ObjectProperty; 

    public DataBinderControlContext 
     (
      DataBinderBindingSourceContext<TDataSource> bindingSourceContext, 
      Expression<Func<TDataSource, TProperty>> objectProperty 
     ) 
    { 
     BindingSourceContext = RequireArg.NotNull(bindingSourceContext); 
     ObjectProperty = ExpressionHelper.GetPropertyName(objectProperty); 
    } 

    public DataBinderControlContext<TDataSource, TProperty> Control<TControl>(TControl control, Expression<Func<TControl, TProperty>> property) 
     where TControl : Control 
    { 
     var controlPropertyName = ExpressionHelper.GetPropertyName(property); 
     control.DataBindings.Add(controlPropertyName, BindingSourceContext.DataSource, ObjectProperty, true); 

     return this; 
    } 
} 

public static class ExpressionHelper 
{ 
    public static string GetPropertyName<TResult>(Expression<Func<TResult>> property) 
    { 
     return GetMemberNames(((LambdaExpression)property).Body).Skip(1).Join("."); 
    } 

    public static string GetPropertyName<T, TResult>(Expression<Func<T, TResult>> property) 
    { 
     return GetMemberNames(((LambdaExpression)property).Body).Join("."); 
    } 

    static IEnumerable<string> GetMemberNames(Expression expression) 
    { 
     if (expression is ConstantExpression || expression is ParameterExpression) 
      yield break; 

     var memberExpression = (MemberExpression)expression; 

     foreach (var memberName in GetMemberNames(memberExpression.Expression)) 
      yield return memberName; 

     yield return memberExpression.Member.Name; 
    } 
} 

public static class StringExtentions 
{ 
    public static string Join(this IEnumerable<string> values, string separator) 
    { 
     if (values == null) 
      return null; 

     return string.Join(separator, values.ToArray()); 
    } 
} 
0

Bạn có thể sử dụng phản ánh để tìm tên ;-)

Điều này tất nhiên sẽ là một tham chiếu vòng tròn, bạn muốn sử dụng tên mà bạn nghĩ rằng đó là để tìm cùng tên (hoặc không tìm thấy bất cứ điều gì , có nghĩa là tài sản đã được đổi tên ... Nhưng có một ý tưởng (hay đúng hơn là một thủ thuật): bằng cách tạo một tham chiếu không làm gì cho thuộc tính bạn muốn sử dụng, bạn sẽ nhận được xác nhận thời gian biên dịch rằng nó vẫn còn đó. vấn đề là nếu ai đó chỉ đổi tên thuộc tính khác nhau xung quanh, trong trường hợp đó, tên vẫn tồn tại (không có lỗi biên dịch), nhưng có ngữ nghĩa cấp ứng dụng khác nhau (có thể xảy ra bất ngờ trong đầu ra của ứng dụng)

4

Hãy xem này code snippet Tôi đã đăng trong một câu hỏi khác, nó có thể giúp bạn! (Nhưng mà thôi, nếu bạn đang sử dụng .NET 3.5)

Trân trọng
Oliver Hanappi

4

Bạn có thể làm điều đó với cây biểu hiện, như được giải thích in this question

protected static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression) 
{ 
    if (expression.NodeType == ExpressionType.Lambda && expression.Body.NodeType == ExpressionType.MemberAccess) 
    { 
     PropertyInfo prop = (expression.Body as MemberExpression).Member as PropertyInfo; 
     if (prop != null) 
     { 
      return prop.Name; 
     } 
    } 
    throw new ArgumentException("expression", "Not a property expression"); 
} 

... 

ageControl.Bind(p, GetPropertyName((Person p) => p.Age));