Đối với .NET 2.0, đây là một đoạn mã đẹp mà tôi đã viết chính xác những gì bạn muốn và hoạt động cho bất kỳ thuộc tính nào trên Control
:
private delegate void SetControlPropertyThreadSafeDelegate(
Control control,
string propertyName,
object propertyValue);
public static void SetControlPropertyThreadSafe(
Control control,
string propertyName,
object propertyValue)
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate
(SetControlPropertyThreadSafe),
new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(
propertyName,
BindingFlags.SetProperty,
null,
control,
new object[] { propertyValue });
}
}
Gọi nó như thế này:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Nếu bạn đang sử dụng .NET 3.0 trở lên, bạn có thể viết lại phương thức trên dưới dạng phương thức mở rộng của Control
lớp, sau đó sẽ đơn giản hóa cuộc gọi đến:
myLabel.SetPropertyThreadSafe("Text", status);
CẬP NHẬT 05/10/2010:
Đối với .NET 3.0, bạn nên sử dụng mã này:
private delegate void SetPropertyThreadSafeDelegate<TResult>(
Control @this,
Expression<Func<TResult>> property,
TResult value);
public static void SetPropertyThreadSafe<TResult>(
this Control @this,
Expression<Func<TResult>> property,
TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member
as PropertyInfo;
if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(
propertyInfo.Name,
propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}
if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate<TResult>
(SetPropertyThreadSafe),
new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(
propertyInfo.Name,
BindingFlags.SetProperty,
null,
@this,
new object[] { value });
}
}
trong đó sử dụng các biểu thức LINQ và lambda để cho phép cú pháp sạch hơn, đơn giản hơn và an toàn hơn:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Bây giờ, không chỉ tên thuộc tính được kiểm tra tại thời gian biên dịch, loại thuộc tính cũng vậy, do đó, không thể (ví dụ) gán giá trị chuỗi cho thuộc tính boolean và do đó gây ra ngoại lệ thời gian chạy.
Thật không may, điều này không ngăn cản bất cứ ai làm những điều ngu ngốc như chuyển vào Control
tài sản và giá trị của người khác, vì vậy những điều sau đây sẽ vui vẻ biên dịch:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Do đó tôi đã thêm các kiểm tra thời gian chạy để đảm bảo rằng thuộc tính được truyền thực sự thuộc về Control
phương thức đang được gọi. Không hoàn hảo, nhưng vẫn tốt hơn rất nhiều so với phiên bản .NET 2.0.
Nếu bất cứ ai có bất kỳ đề xuất nào thêm về cách cải thiện mã này để đảm bảo an toàn trong thời gian biên dịch, vui lòng bình luận!