共用方式為


疑難排解例外狀況:System.InvalidOperationException

呼叫物件的方法時,若物件的狀態無法支援方法呼叫,會擲回 InvalidOperationException。 方法嘗試從非主執行緒或 UI 執行緒的執行緒操作 UI 時,也會擲回例外狀況。

重要

因為 可能在各種不同的情況下擲回 InvalidOperationException,所以請務必閱讀並了解例外狀況助理中所顯示的 Message

本文內容

在非 UI 執行緒上執行以更新 UI 的方法

變更其逐一查看的集合之 foreach (Visual Basic 中的 For Each) 區塊中的陳述式

將 Null 轉換成 T 的 Nullable<T>

在空集合上呼叫的 System.Linq.Enumerable 方法

相關文章

本文中的程式碼範例,示範一些會在您的應用程式中發生的常見 InvalidOperationException 例外狀況。 處理問題的方式需視特定情況而定。 如果對應用程式功能而言是嚴重錯誤的例外狀況,您可能要使用 try … catch (Visual Basic 中的 Try Catch) 建構函式來擷取例外狀況,並清除應用程式的狀態,再結束應用程式。 但可以預測並避免其他 InvalidOperationException。 修改過的方法範例會示範一些此類技術。

在非 UI 執行緒上執行以更新 UI 的方法

從非 UI 執行緒更新 UI 造成 InvalidOperationException | 避免非 UI 執行緒上的 InvalidOperationExceptions

大部分 .NET GUI (圖形化使用者介面) 應用程式架構,例如 Windows Form 和 Windows Presentation Foundation (WPF),可讓您只從建立及管理 UI 的執行緒 (主要UI 執行緒) 存取 GUI 物件。 嘗試從非 UI 執行緒存取 UI 元素時,會擲回 InvalidOperationException

從非 UI 執行緒更新 UI 造成 InvalidOperationException

注意事項注意事項

下列範例使用 以工作為基礎的非同步模式 (TAP) 建立非 UI 執行緒。不過,這些範例也與所有.NET 非同步程式設計模式 相關。

在這些範例中,ThreadsExampleBtn_Click 事件處理常式會呼叫 DoSomeWork 方法兩次。 第一次呼叫的方法 (DoSomeWork(20); 會同步執行並成功。 但第二次呼叫 (Task.Run( () => { DoSomeWork(1000);});) 會在應用程式執行緒集區中的執行緒上執行。 由於這個呼叫會嘗試從非 UI 執行緒更新 UI,所以此陳述式擲回 InvalidOperationException

WPF 和市集應用程式

注意事項注意事項

在手機的市集應用程式中,會擲回 Exception,而不是更特定的 InvalidOperationException

例外狀況訊息:

WPF 應用程式

其他資訊: 呼叫執行緒無法存取此物件,因為此物件屬於另一個執行緒。

市集應用程式

其他資訊: 應用程式所呼叫了整理給不同執行緒的介面。 (但 HRESULT: 0x8001010E (RPC_E_WRONG_THREAD) 除外)

private async void ThreadsExampleBtn_Click(object sender, RoutedEventArgs e)
{
    TextBox1.Text = String.Empty;

    TextBox1.Text = "Simulating work on UI thread.\n";
    DoSomeWork(20);

    TextBox1.Text += "Simulating work on non-UI thread.\n";
    await Task.Run(() => DoSomeWork(1000));

    TextBox1.Text += "ThreadsExampleBtn_Click completes. ";
}

private void DoSomeWork(int msOfWork)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork);
    TextBox1.Text += msg;
}

Windows Forms 應用程式

例外狀況訊息:

  • 其他資訊: 跨執行緒作業無效: 存取控制項 'TextBox1' 時所使用的執行緒與建立控制項的執行緒不同。
private async void ThreadsExampleBtn_Click(object sender, EventArgs e)
{
    TextBox1.Text = String.Empty;

    var tbLinesList = new List<string>() {"Simulating work on UI thread."};
    TextBox1.Lines = tbLinesList.ToArray();
    DoSomeWork(20, tbLinesList);

    tbLinesList.Add("Simulating work on non-UI thread.");
    TextBox1.Lines = tbLinesList.ToArray();
    await Task.Run(() => DoSomeWork(1000, tbLinesList));

    tbLinesList.Add("ThreadsExampleBtn_Click completes.");
    TextBox1.Lines = tbLinesList.ToArray();
}
private void DoSomeWork(int msOfWork, List<string> tbLinesList)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime) { };
    {
        // spin
    };

    // report completion
    var msg = String.Format("Some work completed in {0} ms on UI thread. \n", msOfWork);
    tbLinesList.Add(msg);
    TextBox1.Lines = tbLinesList.ToArray();
}

回到頁首本文內容本節內容本節內容

避免非 UI 執行緒上的 InvalidOperationExceptions

Windows UI 架構實作了「發送器」(dispatcher) 模式,其中包含檢查是否正在 UI 執行緒上執行對 UI 元素成員呼叫的方法,以及在 UI 執行緒上排定呼叫的其他方法。

WPF 應用程式

在 WPF 應用程式中,使用其中一個 Dispatcher.Invoke 方法來執行 UI 執行緒上的委派。 如果有必要,請使用 Dispatcher.CheckAccess 方法,以判斷某個方法是否執行於非 UI 執行緒上。

private void DoSomeWork(int msOfWork)
{
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
    var msg = String.Empty;
    if (TextBox1.Dispatcher.CheckAccess())
    {
        msg = String.Format(msgFormat, msOfWork, String.Empty);
        TextBox1.Text += msg;
    }
    else
    {
        msg = String.Format(msgFormat, msOfWork, "non-");
        Action act = ()=> {TextBox1.Text += msg;};
        TextBox1.Dispatcher.Invoke(act);
    }
}

Windows Forms 應用程式

在 Windows Form 應用程式中,使用 Control.Invoke 方法來執行更新 UI 執行緒的委派。 如果有必要,請使用 Control.InvokeRequired 屬性,以判斷某個方法是否執行於非 UI 執行緒上。

private void DoSomeWork(int msOfWork, List<string> tbLinesList)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
    var msg = String.Empty;
    if (TextBox1.InvokeRequired)
    {
        msg = String.Format(msgFormat, msOfWork, "non-");
        tbLinesList.Add(msg);
        Action act = () => TextBox1.Lines = tbLinesList.ToArray();
        TextBox1.Invoke( act );
    }
    else
    {
        msg = String.Format(msgFormat, msOfWork, String.Empty);
        tbLinesList.Add(msg);
        TextBox1.Lines = tbLinesList.ToArray();
    }
}

市集應用程式

在市集應用程式中,使用 CoreDispatcher.RunAsync 方法來執行更新 UI 執行緒的委派。 如果有必要,請使用 HasThreadAccess 屬性,以判斷某個方法是否執行於非 UI 執行緒上。

private void DoSomeWork(int msOfWork)
{
    // simulate work
    var endTime = DateTime.Now.AddMilliseconds(msOfWork);
    while (DateTime.Now < endTime)
    {
        // spin
    };

    // report completion
    var msgFormat = "Some work completed in {0} ms on {1}UI thread.\n";
    var msg = String.Empty;

    if (TextBox1.Dispatcher.HasThreadAccess)
    {
        msg = String.Format(msgFormat, msOfWork, String.Empty);
        TextBox1.Text += msg;
    }
    else
    {
        msg = String.Format(msgFormat, msOfWork, "non-");
        TextBox1.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,  
            ()=> {TextBox1.Text += msg;});
    }
}

回到頁首本文內容本節內容本節內容

變更其逐一查看的集合之 foreach (Visual Basic 中的 For Each) 區塊中的陳述式

使用 foreach 造成 InvalidOperationException | 避免迴圈中的 InvalidOperationExceptions

Foreach 陳述式 (Visual Basic 中的 For Each),會為陣列或集合中實作 IEnumerableIEnumerable 介面的每個元素,重複內嵌的陳述式群組。 foreach 陳述式可用來逐一查看集合,以讀取和修改元素,但它不能用來新增或移除集合中的元素。 如果您在 foreach 陳述式中,新增或移除來源集合的元素,會擲回 InvalidOperationException

使用 foreach 造成 InvalidOperationException

在這個範例中,numList.Add(5); 陳述式嘗試修改 foreach 區塊中的清單時,就會擲回 InvalidOperationException

例外狀況訊息:

  • 其他資訊: 集合已修改; 列舉操作可能無法執行。
private void AddElementsToAList()
{
    var numList = new List<int>() { 1, 2, 3, 4 };

    foreach (var num in numList)
    {
        if (num == 2)
        {
            numList.Add(5);
        }
    }

    // Display list elements
    foreach (var num in numList)
    {
        Console.Write("{0} ", num);
    }
}

回到頁首本文內容本節內容本節內容

避免迴圈中的 InvalidOperationExceptions

重要

逐一查看集合時,如果對清單新增或移除元素,可能會有非預期且很難預測的副作用。如果可能的話,您應該移動反覆運算之外的作業。

private void AddElementsToAList ()
{
    var numList = new List<int>() { 1, 2, 3, 4 };

    var numberOf2s = 0;

    foreach (var num in numList)
    {
        if (num == 2)
        {
            numberOf2s++;
        }
    }

    for (var i = 0; i < numberOf2s; i++ ) 
    { 
        numList.Add(5); 
    }

    // Display list elements
    foreach (var num in numList)
    {
        Console.Write("{0} ", num);
    }
}

如果您的情況需要在逐一查看集合時,對清單新增或移除元素,請使用 for (Visual Basic 中的 For) 迴圈:

private void AddElementsToAList ()
{
    var numList = new List<int>() { 1, 2, 3, 4 };

    for (var i = 0; i < numList.Count; i++) 
    {
        if (numList[i] == 2)
        {
            numList.Add(5);
        }
    }

    // Display list elements
    foreach (var num in numList)
    {
        Console.Write("{0} ", num);
    }
}

回到頁首本文內容本節內容本節內容

將 Null 轉換成 T 的 Nullable<T>

無效的轉換造成 InvalidOperationException | 從錯誤的轉換避免 InvalidOperationException

如果您轉換的 Nullable 結構,是將 null (Visual Basic 中的 Nothing) 轉為其基礎類型,會擲回 InvalidOperationException 例外狀況。

無效的轉換造成 InvalidOperationException

在這個方法中,Select 方法將 dbQueryResults 的 Null 元素轉換為 int 時,會擲回 InvalidOperationException

例外狀況訊息:

  • 其他資訊: 可為 Null 的物件必須具有值。
private void MapQueryResults()
{
    var dbQueryResults = new int?[] { 1, 2, null, 4 };

    var ormMap = dbQueryResults.Select(nullableInt => (int)nullableInt);

    //display map list
    foreach (var num in ormMap)
    {
        Console.Write("{0} ", num);
    }
    Console.WriteLine();
}

回到頁首本文內容本節內容本節內容

從錯誤的轉換避免 InvalidOperationException

若要避免 InvalidOperationException

Nullable<T>.HasValue 範例

private void MapQueryResults()
{
    var dbQueryResults = new int?[] { 1, 2, null, 4 };

    var ormMap = dbQueryResults
        .Where(nulllableInt => nulllableInt.HasValue)
        .Select(num => (int)num);

    //display map list
    foreach (var num in ormMap)
    {
        Console.Write("{0} ", num);
    }
    Console.WriteLine();

    // handle nulls
    Console.WriteLine("{0} nulls encountered in dbQueryResults",
        dbQueryResults.Where(nullableInt => !nullableInt.HasValue).Count());
}

Nullable<T>.GetValueOrDefault 範例

在這個範例中,我們使用 GetValueOrDefault(UTP),指定 dbQueryResults 所傳回之值預期值以外的預設值。

private void MapQueryResults()
{
    var dbQueryResults = new int?[] { 1, 2, null, 4 };
    var nullFlag = int.MinValue;

    var ormMap = dbQueryResults.Select(nullableInt => nullableInt.GetValueOrDefault(nullFlag));

    // handle nulls
    Console.WriteLine("'{0}' indicates a null database value.", nullFlag);

    foreach (var num in ormMap)
    {
        Console.Write("{0} ", num);
    }
    Console.WriteLine();
}

回到頁首本文內容本節內容本節內容

在空集合上呼叫的 System.Linq.Enumerable 方法

Enumerable 方法 AggregateAverageLastMaxMinFirstSingleSingleOrDefault 會在某個序列上執行作業,並傳回單一結果。

序列若是空的,這些方法的某些多載會擲回 InvalidOperationException 例外狀況;而其他多載會傳回 null (Visual Basic 中的 Nothing)。 若序列包含一個以上的元素,SingleOrDefault 也會擲回 InvalidOperationException

提示

本節中討論的大部分 Enumerable 方法都是多載。請確定您了解所選擇之多載的行為。

例外狀況訊息:

Aggregate、Average、Last、Max 和 Min 方法 | First 和 FirstOrDefault 方法 | Single 和 SingleOrDefault 方法

Aggregate、Average、Last、Max 和 Min 方法

  • 其他資訊: 序列未包含元素

使用 Average 造成 InvalidOperationException

在這個範例中,下列方法會在呼叫 Average 方法時,擲回 InvalidOperationException,因為 Linq 運算式傳回序列,其中沒有包含大於 4 的元素。

private void FindAverageOfNumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    var avg = dbQueryResults.Where(num => num > 4).Average();

    Console.WriteLine("The average value numbers greater than 4 in the returned records is {0}", avg);
}

當您使用這些方法之一,且沒有檢查空的序列時,便是隱含假設序列不是空的,而空的序列是未預期的元素。 當您假設序列不是空的,則捕捉或擲回例外狀況就很恰當。

避免因空的序列造成 InvalidOperationException

使用其中一種 Enumerable.Any 方法,檢查序列是否是空的。

提示

如果 Average 的序列可能包含大量元素,或如果產生序列的作業是高度耗費資源,則使用 Any 可改善檢查的效能。

private void FindAverageOfNumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    var moreThan4 = dbQueryResults.Where(num => num > 4);

    if(moreThan4.Any())
    {
        var msgFormat = "The average value numbers greater than 4 in the returned records is {0}";
        Console.WriteLine(msgFormat, moreThan4.Average());
    }
    else
    {
        // handle empty collection 
        Console.WriteLine("There are no values greater than 4 in the ecords.");
    }
}

回到頁首本文內容本節內容本節內容

First 和 FirstOrDefault 方法

First 會傳回序列中的第一個元素,或擲回InvalidOperationException (如果序列是空的)。 您可以呼叫 FirstOrDefault 方法,而不是First,以傳回指定或預設值,而不是擲回例外狀況。

注意事項注意事項

在 .NET Framework 中,類型內含有預設值的概念。例如,任何參考類型的預設值為 Null,整數類型則為零。請參閱預設值表 (C# 參考)

使用 First 造成 InvalidOperationException

First 是最佳化方法,會傳回序列中符合指定條件的第一個元素。 如果找不到滿足條件的元素,方法會擲回 InvalidOperationException 例外狀況。

在這個範例中,First 方法會擲回例外狀況,因為 dbQueryResults 沒有包含大於 4 的元素。

例外狀況訊息:

  • 其他資訊: 序列未包含符合的元素
private void FindANumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    var firstNum = dbQueryResults.First(n=> n > 4);

    var msgFormat = "{0} is an element of dbQueryResults that is greater than 4";
    Console.WriteLine(msgFormat, firstNum);

}

使用 FirstOrDefault 避免例外狀況

您可以使用其中一個 FirstOrDefault 方法,取代 First,以檢查 firstNum 序列是否包含至少一個元素。 如果查詢中找不到滿足條件的元素,它會傳回指定值或預設值。 您可以檢查傳回的值,以判斷是否找到任何元素。

注意事項注意事項

如果類型的預設值在序列中是有效的元素,使用 FirstOrDefault 可能會在實作上比較複雜。

private void FindANumbersGreaterThan4()
{
    var dbQueryResults = new[] { 1, 2, 3, 4 };

    // default(object) == null
    var firstNum = dbQueryResults.FirstOrDefault(n => n > 4);

    if (firstNum != 0)
    {
        var msgFormat = "{0} is an element of dbQueryResults that is greater than 4";
        Console.WriteLine(msgFormat, firstNum);
    }
    else
    {
        // handle default case
        Console.WriteLine("No element of dbQueryResults is greater than 4.");
    }
}

回到頁首本文內容本節內容本節內容

Single 和 SingleOrDefault 方法

Enumerable.Single 方法會傳回序列的唯一元素,或符合指定測試的序列中唯一元素。

如果序列中沒有任何元素,或序列中有一個以上的元素,方法會擲回 InvalidOperationException 例外狀況。

當序列中沒有包含任何元素時,您可以使用 SingleOrDefault,傳回指定值或預設值,而不是擲回例外狀況。 不過,當序列包含一個以上符合選取述詞的元素時,SingleOrDefault 仍會擲回 InvalidOperationException

注意事項注意事項

在 .NET Framework 中,類型內含有預設值的概念。例如,任何參考類型的預設值為 Null,整數類型則為零。請參閱預設值表 (C# 參考)

使用 Single 造成 InvalidOperationExceptions

在這個範例中,singleObject 會擲回InvalidOperationException,因為 dbQueryResults 沒有包含大於 4 的元素。

例外狀況訊息:

  • 其他資訊: 序列未包含符合的元素
private void FindTheOnlyNumberGreaterThan4()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };

    var singleObject = dbQueryResults.Single(obj => (int)obj > 4);

    // display results
    var msgFormat = "{0} is the only element of dbQueryResults that is greater than 4";
    Console.WriteLine(msgFormat, singleObject);
}

使用 Single 或 SingleOrDefault 造成 InvalidOperationExceptions

在這個範例中,singleObject 會擲回InvalidOperationException,因為 dbQueryResults 包含一個以上大於 2 的元素。

例外狀況訊息:

  • 其他資訊: 序列包含一個以上相符的元素
private void FindTheOnlyNumberGreaterThan2()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };

    var singleObject = dbQueryResults.SingleOrDefault(obj => (int)obj > 2);

    if (singleObject != null)
    {
        var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2";
        Console.WriteLine(msgFormat, singleObject);
    }
    else
    {
        // handle empty collection
        Console.WriteLine("No element of dbQueryResults is greater than 2.");
    }
}

使用 Single 避免 InvalidOperationExceptions

使用 SingleSingleOrDefault 也可以做為您要的情況。 Single 強烈意味著您需要從條件傳回只有一個結果。 SingleOrDefault 會宣告您需要一個或零個結果,除此之外不要更多。 這些條件無效時,擲回或捕捉 InvalidOperationException 就很恰當。 但是,如果您需要無效的條件以某些頻率發生,應該考慮使用其他 Enumerable 方法,例如 FirstWhere,以產生您的結果。

在開發期間,您可以使用其中一個 Assert 方法來檢查您的假設。 在這個範例中,強調顯示的程式碼會在開發期間導致偵錯工具中斷,並顯示判斷提示對話方塊。 判斷提示已在發行程式碼中移除,且如果結果無效,會擲回任何 Single

注意事項注意事項

使用 Take``1,並設定其 count 參數為 2,將傳回的序列限制為最多兩個元素。此序列包括所有您需要檢查的情況 (0、1 和 1 個以上的元素),而且當序列可能包含大量的元素,或產生序列的作業會高度耗費資源,也可改進檢查的效能。

private void FindTheOnlyNumberGreaterThan4()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };
    var moreThan4 = dbQueryResults.Where(obj => (int)obj > 4).Take(2);

    System.Diagnostics.Debug.Assert(moreThan4.Count() == 1, 
        String.Format("moreThan4.Count() == 1; Actual count: {0}", moreThan4.Count()));

    // do not handle exceptions in release code
    Console.WriteLine("{0} is the only element of dbQueryResults that is greater than 4", 
        moreThan4.Single());
}

如果您想要避免此例外狀況,同時處理發行程式碼中的無效狀態,可以修改上述的技巧。 在這個範例中,這個方法會回應 switch 陳述式中,moreThan2 所傳回的元素數目。

private void FindTheOnlyNumberGreaterThan2()
{
    var dbQueryResults = new[] { (object)1, (object)2, (object)3, (object)4 };

    var moreThan2 = dbQueryResults.TakeWhile(obj => (int)obj > 2).Take(2);

    switch(moreThan2.Count())
    { 
        case 1:
            // success
            var msgFormat = "{0} is the only element of dbQueryResults that is greater than 2";
            Console.WriteLine(msgFormat, moreThan2.Single());
            break;
        case 0:
            // handle empty collection
            Console.WriteLine("No element of the dbQueryResults are greater than 4.");
            break;
        default: // count > 1
            // handle more than one element
            Console.WriteLine("More than one element of dbQueryResults is greater than 4");
            break;
    }
}

回到頁首本文內容本節內容本節內容

相關文章

例外狀況的設計方針 (.NET Framework 設計方針)

處理和擲回例外狀況 (.NET Framework 應用程式基本功能) (機器翻譯)

如何:接收 First-Chance 例外狀況通知 (.NET Framework 開發指南) (機器翻譯)

如何:處理 PLINQ 查詢中的例外狀況 (.NET Framework 開發指南) (機器翻譯)

Managed 執行緒中的例外狀況 (.NET Framework 開發指南) (機器翻譯)

例外狀況與例外狀況處理 (C# 程式設計指南) (機器翻譯)

例外狀況處理陳述式 (C# 參考) (機器翻譯)

Try...Catch...Finally 陳述式 (Visual Basic) (機器翻譯)

例外狀況處理 (F#) (機器翻譯)

C++/CLI 中的例外狀況 (機器翻譯)

例外狀況處理 (工作平行程式庫) (機器翻譯)

例外狀況處理 (偵錯) (機器翻譯)

逐步解說:處理並行例外狀況 (在 Visual Studio 中存取資料) (機器翻譯)

如何:處理資料繫結時所發生的錯誤和例外狀況 (Windows Forms) (機器翻譯)

處理網路應用程式中的例外狀況 (XAML) (Windows)

回到頁首本文內容