Untuk .NET 2.0, berikut ini sedikit kode yang saya tulis yang melakukan persis seperti yang Anda inginkan, dan berfungsi untuk semua properti di 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 });
}
}
Sebut saja seperti ini:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Jika Anda menggunakan .NET 3.0 atau di atasnya, Anda bisa menulis ulang metode di atas sebagai metode ekstensi Control
kelas, yang kemudian akan menyederhanakan panggilan ke:
myLabel.SetPropertyThreadSafe("Text", status);
PEMBARUAN 05/10/2010:
Untuk .NET 3.0 Anda harus menggunakan kode ini:
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 });
}
}
yang menggunakan ekspresi LINQ dan lambda untuk memungkinkan sintaks yang lebih bersih, lebih sederhana dan lebih aman:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Tidak hanya nama properti sekarang diperiksa pada waktu kompilasi, tipe properti juga, jadi tidak mungkin untuk (misalnya) menetapkan nilai string ke properti boolean, dan karenanya menyebabkan pengecualian runtime.
Sayangnya ini tidak menghentikan siapa pun dari melakukan hal-hal bodoh seperti melewati Control
properti dan nilai orang lain, jadi yang berikut akan dengan senang hati dikompilasi:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Oleh karena itu saya menambahkan cek runtime untuk memastikan bahwa properti yang lewat benar-benar milik Control
metode yang dipanggil. Tidak sempurna, tetapi masih jauh lebih baik daripada versi .NET 2.0.
Jika ada yang punya saran lebih lanjut tentang cara meningkatkan kode ini untuk keamanan waktu kompilasi, silakan berkomentar!