为什么从其他线程更新System.Windows.Forms.Control
的部分属性与不安全且程序员必须使用Delegate
的其他属性相比是否安全?
例如,ForeColor
与Text
进行比较。
有人可以从设计角度解释这个吗?
答案 0 :(得分:4)
要回答您的问题,为什么某些属性访问会引发非法的跨线程错误而其他属性不会引发错误,您可以参考source code for System.Windows.Forms.Control
。它需要深入挖掘它,但看起来似乎有些访问被建议为线程安全,例如获取Text
属性但设置它不是。
实际上,将所有控制访问视为非线程安全的传统智慧是最佳实践。
public virtual string Text {
get {
if (CacheTextInternal) {
return(text == null) ? "" : text;
}
else {
return WindowText;
}
}
set {
if (value == null) {
value = "";
}
if (value == Text) {
return;
}
if (CacheTextInternal) {
text = value;
}
WindowText = value;
OnTextChanged(EventArgs.Empty);
if( this.IsMnemonicsListenerAxSourced ){
for( Control ctl = this; ctl != null; ctl = ctl.ParentInternal ) {
ActiveXImpl activeXImpl = (ActiveXImpl)ctl.Properties.GetObject(PropActiveXImpl);
if( activeXImpl != null ) {
activeXImpl.UpdateAccelTable();
break;
}
}
}
}
}
请注意在上面的代码中使用内部WindowText属性。
/// <devdoc>
/// The current text of the Window; if the window has not yet been created, stores it in the control.
/// If the window has been created, stores the text in the underlying win32 control.
/// This property should be used whenever you want to get at the win32 control's text. For all other cases,
/// use the Text property - but note that this is overridable, and any of your code that uses it will use
/// the overridden version in controls that subclass your own.
/// </devdoc>
internal virtual string WindowText {
get {
if (!IsHandleCreated) {
if (text == null) {
return "";
}
else {
return text;
}
}
using (new MultithreadSafeCallScope()) {
// it's okay to call GetWindowText cross-thread.
//
int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(window, Handle));
// Check to see if the system supports DBCS character
// if so, double the length of the buffer.
if (SystemInformation.DbcsEnabled) {
textLen = (textLen * 2) + 1;
}
StringBuilder sb = new StringBuilder(textLen + 1);
UnsafeNativeMethods.GetWindowText(new HandleRef(window, Handle), sb, sb.Capacity);
return sb.ToString();
}
}
set {
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
}
请注意MultithreadSafeCallScope
代码中get
的使用。还要注意使用会引发跨线程错误的Handle
属性;我相信Handle
属性充当 gate-keeper ,用于检查跨线程访问。
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}