反映叫用 API 例外狀況的變更
呼叫反映叫用 API 變更時擲回的例外狀況。
先前的行為
先前,藉傳址傳回值的叫用方式傳回
null
時,會擲回 NullReferenceException。針對建構函式,則擲回下列例外狀況:
- 暫時性例外狀況,包括 OutOfMemoryException。
- 當 OverflowException 為數組的長度參數傳遞負值時。
在沒有
ref
修飾元的情況針對類似 byref 的參數傳遞null
時 (也就是以值傳遞),沒有擲回例外狀況,且執行時間會以預設值取代 Null 值。
新的行為
從 .NET 7 開始:
在驗證初始
Invoke()
參數之後,不會擲回原始例外狀況 (包括先前行為提及的 NullReferenceException 和 OutOfMemoryException),而是於所有案例擲回 TargetInvocationException。 內部例外狀況包含原始例外狀況。當參數宣告為「by value」時,針對 類似 byref 的參數傳遞
null
時會擲回 NotSupportedException (也就是缺少ref
修飾元)。 如果是藉傳址方式傳遞參數的相關案例 (也就是具有ref
修飾元),則先前和新的行為相同:會擲回 NotSupportedException。
導入的版本
.NET 7
中斷性變更的類型
這項變更會影響二進位相容性。
變更原因
擲回 TargetInvocationException 而不是原始例外狀況,可讓體驗更一致。 這將傳入參數驗證所造成的例外狀況 (這些參數不會與 TargetInvocationException 一起包裝),與因實作目標方法而擲回的例外狀況 (已包裝) 進行適當地分層。 一致的規則可在 CLR 和 Invoke
API 的不同實作之間提供更一致的體驗。
將類似 byref 的類型傳遞至 Invoke()
API 時,擲回 NotSupportedException 的變更會修正原始實作的監督,而不會擲回。 原始實作給人 Invoke()
API 所支援 ref struct
類型的印象,但事實並非如此。 由於目前的 Invoke()
API 使用 System.Object 作為參數類型,且 ref struct
類型無法以 boxed 處理為 System.Object,因此是不支援的案例。
建議的動作
如果您在呼叫 Invoke()
時未使用 BindingFlags.DoNotWrapExceptions,而且針對 TargetInvocationException 以外的例外狀況,您有 Invoke()
API 周圍的陳述式 catch
,請考慮變更或移除這些 catch
陳述式。 其他例外狀況將不會再因叫用而擲回。 不過,如果您從嘗試叫用目標方法之前發生的引數驗證攔截例外狀況,您應該保留這些 catch
陳述式。 在嘗試叫用之前驗證的引數會擲回,而每有與 TargetInvocationException 一同包裝,且不會變更語意。
請考慮使用 BindingFlags.DoNotWrapExceptions,這樣才永遠不會擲回 TargetInvocationException。 在此情況下,原始例外狀況不會由 TargetInvocationException 包裝。 大部分情況下,不包裝例外狀況可改善診斷實際問題的機會,因為並非所有例外狀況報告工具都會顯示內部例外狀況。 此外,使用 BindingFlags.DoNotWrapExceptions 將會擲回與直接呼叫方法相同的例外狀況(不需要反映)。 多數情況下這是理想的做法,因為使用反映的選擇可以是任意的,或是不需要呈現給呼叫端的實作詳細資料。
少數情況下您必須透過反映將預設值傳遞至方法,其中包含傳遞「by value」的類似 byref 參數的方法,您可以新增省略參數的包裝函式方法,並使用該參數的預設值呼叫目標方法。
受影響的 API
- System.Reflection.MethodBase.Invoke(Object, Object[])
- System.Reflection.MethodBase.Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.ConstructorInfo.Invoke(Object[])
- System.Reflection.ConstructorInfo.Invoke(BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.PropertyInfo.GetValue(Object)
- System.Reflection.PropertyInfo.GetValue(Object, Object[])
- System.Reflection.PropertyInfo.GetValue(Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.PropertyInfo.SetValue(Object, Object)
- System.Reflection.PropertyInfo.SetValue(Object, Object, Object[])
- System.Reflection.PropertyInfo.SetValue(Object, Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.Emit.DynamicMethod.Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Activator.CreateInstance(Type, Object[])
- System.Activator.CreateInstance(Type, Object[], Object[])
- System.Activator.CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo)
- System.Activator.CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo, Object[])
- System.Activator.CreateInstance(String, String, Boolean, BindingFlags, Binder, Object[], CultureInfo, Object[])