Для .NET 2.0, ось вам написаний чудовий фрагмент коду, який робить саме те, що ви хочете, і працює для будь-якої власності на 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 });
}
}
Назвіть це так:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Якщо ви використовуєте .NET 3.0 або вище, ви можете переписати вищевказаний метод як метод розширення Control
класу, який потім спростить виклик:
myLabel.SetPropertyThreadSafe("Text", status);
ОНОВЛЕННЯ 10.10.2010:
Для .NET 3.0 слід використовувати цей код:
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 });
}
}
який використовує вирази LINQ та лямбда, щоб забезпечити набагато більш чистий, простий і безпечний синтаксис:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Ім'я властивості перевіряється не тільки під час компіляції, але й тип властивості, тому неможливо (наприклад) присвоїти значення рядка булевому властивості, а отже, викликати виняток під час виконання.
На жаль, це нікого не заважає робити такі дурні речі, як передача у Control
власність та цінність чужого, тому наступне буде щасливо складено:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Отже, я додав перевірки часу виконання, щоб переконатися, що властивість, що передається, насправді належить до того, Control
що використовується методом. Не ідеально, але все ж набагато краще, ніж версія .NET 2.0.
Якщо хтось має будь-які додаткові пропозиції щодо вдосконалення цього коду для безпеки під час компіляції, будь ласка, прокоментуйте!