IndexOutOfRangeException / ArgumentOutOfRangeException क्या है और मैं इसे कैसे ठीक करूं?

Jan 06 2014

मेरे पास कुछ कोड हैं और जब यह निष्पादित होता है, तो यह ए IndexOutOfRangeException, कहती है,

अनुसूची, सारणी की सीमाओं से बाहर थी।

इसका क्या मतलब है, और मैं इसके बारे में क्या कर सकता हूं?

उपयोग की जाने वाली कक्षाओं के आधार पर यह भी हो सकता है ArgumentOutOfRangeException

Mscorlib.dll में 'System.ArgumentOutOfRangeException' प्रकार का एक अपवाद हुआ, लेकिन उपयोगकर्ता कोड में संभाला नहीं गया था अतिरिक्त जानकारी: सूचकांक सीमा से बाहर था। गैर-नकारात्मक और संग्रह के आकार से कम होना चाहिए।

जवाब

238 AdrianoRepetti Jan 06 2014 at 07:19

यह क्या है?

इस अपवाद का अर्थ है कि आप किसी अमान्य अनुक्रमणिका का उपयोग करके सूचकांक द्वारा संग्रह वस्तु तक पहुँचने का प्रयास कर रहे हैं। जब यह संग्रह की निचली सीमा से कम या इसके तत्वों की संख्या के बराबर या उससे अधिक होता है, तो एक इंडेक्स अमान्य होता है।

जब यह फेंक दिया जाता है

दिए गए एक सरणी को देखते हुए:

byte[] array = new byte[4];

आप इस सरणी को 0 से 3 तक एक्सेस कर सकते हैं, इस सीमा के बाहर के मान IndexOutOfRangeExceptionफेंक दिए जाएंगे। यह याद रखें जब आप एक सरणी बनाते हैं और उसका उपयोग करते हैं।

सरणी लंबाई
C # में, आमतौर पर, सरणियाँ 0-आधारित होती हैं। इसका अर्थ है कि पहले तत्व में इंडेक्स 0 है और अंतिम तत्व में इंडेक्स है Length - 1(जहां Lengthएरे में आइटम की कुल संख्या है) इसलिए यह कोड काम नहीं करता है:

array[array.Length] = 0;

इसके अलावा कृपया ध्यान दें कि यदि आपके पास बहुआयामी सरणी है तो आप Array.Lengthदोनों आयामों के लिए उपयोग नहीं कर सकते , आपको उपयोग करना होगा Array.GetLength():

int[,] data = new int[10, 5];
for (int i=0; i < data.GetLength(0); ++i) {
    for (int j=0; j < data.GetLength(1); ++j) {
        data[i, j] = 1;
    }
}

ऊपरी बाउंड इनक्लूसिव नहीं
है निम्नलिखित उदाहरण में हम एक कच्चा बीदिमेमिक सरणी बनाते हैं Color। प्रत्येक आइटम एक पिक्सेल का प्रतिनिधित्व करता है, सूचकांक से (0, 0)हैं (imageWidth - 1, imageHeight - 1)

Color[,] pixels = new Color[imageWidth, imageHeight];
for (int x = 0; x <= imageWidth; ++x) {
    for (int y = 0; y <= imageHeight; ++y) {
        pixels[x, y] = backgroundColor;
    }
}

यह कोड तब विफल हो जाएगा क्योंकि सरणी 0-आधारित है और छवि में अंतिम (निचला-दाएं) पिक्सेल है pixels[imageWidth - 1, imageHeight - 1]:

pixels[imageWidth, imageHeight] = Color.Black;

एक अन्य परिदृश्य में आप ArgumentOutOfRangeExceptionइस कोड के लिए प्राप्त कर सकते हैं (उदाहरण के लिए यदि आप GetPixelएक Bitmapवर्ग पर विधि का उपयोग कर रहे हैं )।

सरणी न बढ़ें
एक सरणी तेज़ है। हर दूसरे संग्रह की तुलना में रैखिक खोज में बहुत तेज है। ऐसा इसलिए है क्योंकि आइटम मेमोरी में सन्निहित हैं इसलिए मेमोरी एड्रेस की गणना की जा सकती है (और वेतन वृद्धि एक अतिरिक्त है)। एक नोड सूची, सरल गणित का पालन करने की आवश्यकता नहीं है! आप इसे एक सीमा के साथ भुगतान करते हैं: वे नहीं बढ़ सकते हैं, यदि आपको अधिक तत्वों की आवश्यकता है तो आपको उस सरणी को फिर से संगठित करना होगा (यदि पुरानी वस्तुओं को एक नए ब्लॉक में कॉपी किया जाना चाहिए) तो यह अपेक्षाकृत लंबा समय लग सकता है। आप उनका आकार बदलते हैं Array.Resize<T>(), यह उदाहरण मौजूदा सरणी में एक नई प्रविष्टि जोड़ता है:

Array.Resize(ref array, array.Length + 1);

मत भूलो कि वैध सूचकांक से 0हैं Length - 1। यदि आप किसी आइटम को केवल प्राप्त करने का प्रयास करते हैं, Lengthतो आपको लगता है IndexOutOfRangeException(यह व्यवहार आपको भ्रमित कर सकता है यदि आपको लगता है कि वे Insertअन्य संग्रह की विधि के समान सिंटैक्स के साथ बढ़ सकते हैं )।

ऐरे में कस्टम लोअर बाउंड
फस्र्ट आइटम के साथ स्पेशल एरर हमेशा इंडेक्स 0 होता है । यह हमेशा सच नहीं होता है क्योंकि आप एक कस्टम लोअर बाउंड के साथ एक सरणी बना सकते हैं:

var array = Array.CreateInstance(typeof(byte), new int[] { 4 }, new int[] { 1 });

उस उदाहरण में, सरणी सूचक 1 से 4 तक मान्य हैं। बेशक, ऊपरी बाध्य को नहीं बदला जा सकता है।

गलत तर्क
यदि आप किसी सरणी का उपयोग करते हैं, तो वह बिना किसी तर्क के (उपयोगकर्ता इनपुट से या फ़ंक्शन उपयोगकर्ता से) आप इस पासवर्ड को प्राप्त कर सकते हैं:

private static string[] RomanNumbers =
    new string[] { "I", "II", "III", "IV", "V" };

public static string Romanize(int number)
{
    return RomanNumbers[number];
}

अनपेक्षित परिणाम
यह अपवाद किसी अन्य कारण से भी फेंका जा सकता है: कन्वेंशन द्वारा, कई खोज फ़ंक्शंस -1 लौट आएंगे (nullables को .NET 2.0 के साथ पेश किया गया है और वैसे भी यह कई वर्षों से उपयोग में एक प्रसिद्ध सम्मेलन है) टी कुछ भी मिल आइए कल्पना करें कि आपके पास स्ट्रिंग के साथ तुलनीय वस्तुओं की एक सरणी है। आप इस कोड को लिखने के लिए सोच सकते हैं:

// Items comparable with a string
Console.WriteLine("First item equals to 'Debug' is '{0}'.",
    myArray[Array.IndexOf(myArray, "Debug")]);

// Arbitrary objects
Console.WriteLine("First item equals to 'Debug' is '{0}'.",
    myArray[Array.FindIndex(myArray, x => x.Type == "Debug")]);

यह विफल हो जाएगा अगर कोई आइटम myArrayखोज स्थिति को संतुष्ट नहीं करेगा क्योंकि Array.IndexOf()-1 वापस आ जाएगा और फिर सरणी पहुंच फेंक देगा।

अगला उदाहरण संख्याओं के दिए गए सेट की घटनाओं की गणना करने के लिए एक भोली मिसाल है (अधिकतम संख्या को जानकर और एक सरणी को वापस करके जहां सूचकांक 0 पर आइटम नंबर 0 का प्रतिनिधित्व करता है, सूचकांक 1 पर आइटम नंबर 1 का प्रतिनिधित्व करता है और इसी तरह):

static int[] CountOccurences(int maximum, IEnumerable<int> numbers) {
    int[] result = new int[maximum + 1]; // Includes 0

    foreach (int number in numbers)
        ++result[number];

    return result;
}

बेशक, यह एक बहुत ही भयानक कार्यान्वयन है, लेकिन जो मैं दिखाना चाहता हूं वह यह है कि यह नकारात्मक संख्याओं और संख्याओं के लिए विफल हो जाएगा maximum

यह कैसे लागू होता है List<T>?

सरणी के रूप में समान मामले - मान्य अनुक्रमित की श्रेणी - 0 ( List's अनुक्रमित हमेशा 0 से शुरू होते हैं) से list.Count- इस सीमा के बाहर के तत्वों तक पहुँच अपवाद का कारण होगा।

ध्यान दें कि उन्हीं मामलों के लिए List<T>फेंकता है ArgumentOutOfRangeExceptionजहाँ सरणियाँ उपयोग करती हैं IndexOutOfRangeException

सरणियों के विपरीत, List<T>खाली शुरू होता है - इसलिए इस अपवाद के लिए बस बनाई गई सूची के आइटम तक पहुंचने की कोशिश कर रहा है।

var list = new List<int>();

सामान्य मामला सूची को अनुक्रमणित करने के समान है (इसके समान Dictionary<int, T>) अपवाद का कारण होगा:

list[0] = 42; // exception
list.Add(42); // correct

IDataReader और कॉलम
कल्पना कीजिए कि आप इस कोड के साथ डेटाबेस से डेटा पढ़ने की कोशिश कर रहे हैं:

using (var connection = CreateConnection()) {
    using (var command = connection.CreateCommand()) {
        command.CommandText = "SELECT MyColumn1, MyColumn2 FROM MyTable";

        using (var reader = command.ExecuteReader()) {
            while (reader.Read()) {
                ProcessData(reader.GetString(2)); // Throws!
            }
        }
    }
}

GetString()फेंक देंगे IndexOutOfRangeExceptionक्योंकि आप डेटासेट में केवल दो कॉलम हैं, लेकिन आप 3 से एक मान प्राप्त करने का प्रयास कर रहे हैं (सूचक हमेशा 0-आधारित होते हैं)।

कृपया ध्यान दें कि इस व्यवहार अधिकांश के साथ साझा किया जाता है IDataReaderकार्यान्वयन ( SqlDataReader, OleDbDataReaderऔर इसी तरह)।

आप एक ही अपवाद प्राप्त कर सकते हैं यदि आप अनुक्रमणिका ऑपरेटर के IDataReader अधिभार का उपयोग करते हैं जो एक स्तंभ नाम लेता है और एक अमान्य स्तंभ नाम पारित करता है।
उदाहरण के लिए मान लें कि आपने Column1 नामक एक कॉलम को पुनः प्राप्त कर लिया है, लेकिन फिर आप उस फ़ील्ड के मान को पुनः प्राप्त करने का प्रयास करते हैं

 var data = dr["Colum1"];  // Missing the n in Column1.

ऐसा इसलिए होता है क्योंकि अनुक्रमणिका ऑपरेटर किसी Colum1 फ़ील्ड के अनुक्रमणिका को पुन: प्राप्त करने का प्रयास करता है जो मौजूद नहीं है। गेटऑर्डिनल विधि इस अपवाद को फेंक देगी जब इसका आंतरिक सहायक कोड "Colum1" के सूचकांक के रूप में -1 देता है।

अन्य
इस अपवाद को फेंक दिए जाने पर एक और (प्रलेखित) मामला है: यदि, में DataView, DataViewSortसंपत्ति के लिए आपूर्ति किया जा रहा डेटा स्तंभ नाम मान्य नहीं है।

कैसे बचें

इस उदाहरण में, मुझे यह मान लेना चाहिए कि सादगी के लिए, कि सरणियाँ सदैव एकरूप और 0-आधारित होती हैं। आप सख्त होने के लिए (या आप एक पुस्तकालय विकसित कर रहे हैं) चाहते हैं, आप बदलना पड़ सकता है 0के साथ GetLowerBound(0)और .Lengthसाथ GetUpperBound(0)(बेशक आप प्रकार के पैरामीटर यदि System.Array, इसके लिए लागू नहीं होता T[])। कृपया ध्यान दें कि इस मामले में, ऊपरी बाउंड समावेशी है तो यह कोड:

for (int i=0; i < array.Length; ++i) { }

इस तरह से फिर से लिखना चाहिए:

for (int i=array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i) { }

कृपया ध्यान दें कि यह अनुमति नहीं है (इसे फेंक देंगे InvalidCastException), यही कारण है कि यदि आपके पैरामीटर T[]कस्टम लो बाउंड एरेज़ के बारे में सुरक्षित हैं:

void foo<T>(T[] array) { }

void test() {
    // This will throw InvalidCastException, cannot convert Int32[] to Int32[*]
    foo((int)Array.CreateInstance(typeof(int), new int[] { 1 }, new int[] { 1 }));
}

मान्य पैरामीटर
यदि सूचकांक एक पैरामीटर से आता है, तो आपको हमेशा उन्हें मान्य करना चाहिए (उचित ArgumentExceptionया फेंकना ArgumentOutOfRangeException)। अगले उदाहरण में, गलत पैरामीटर का कारण हो सकता है IndexOutOfRangeException, इस फ़ंक्शन के उपयोगकर्ता यह उम्मीद कर सकते हैं क्योंकि वे एक सरणी से गुजर रहे हैं लेकिन यह हमेशा इतना स्पष्ट नहीं होता है। मैं हमेशा सार्वजनिक कार्यों के लिए मापदंडों को मान्य करने का सुझाव दूंगा:

static void SetRange<T>(T[] array, int from, int length, Func<i, T> function)
{
    if (from < 0 || from>= array.Length)
        throw new ArgumentOutOfRangeException("from");

    if (length < 0)
        throw new ArgumentOutOfRangeException("length");

    if (from + length > array.Length)
        throw new ArgumentException("...");

    for (int i=from; i < from + length; ++i)
        array[i] = function(i);
}

यदि फ़ंक्शन निजी है तो आप केवल ifतर्क को बदल सकते हैं Debug.Assert():

Debug.Assert(from >= 0 && from < array.Length);

ऑब्जेक्ट की जाँच करें राज्य
एरे सूचकांक एक पैरामीटर से सीधे नहीं आ सकता है। यह वस्तु स्थिति का हिस्सा हो सकता है। सामान्य तौर पर हमेशा ऑब्जेक्ट स्टेट को सत्यापित करने के लिए एक अच्छा अभ्यास है (यदि आवश्यक हो, तो फ़ंक्शन मापदंडों के साथ और)। आप इसका उपयोग कर सकते हैं Debug.Assert(), एक उचित अपवाद (समस्या के बारे में अधिक वर्णनात्मक) फेंक सकते हैं या इस उदाहरण में संभाल सकते हैं:

class Table {
    public int SelectedIndex { get; set; }
    public Row[] Rows { get; set; }

    public Row SelectedRow {
        get {
            if (Rows == null)
                throw new InvalidOperationException("...");

            // No or wrong selection, here we just return null for
            // this case (it may be the reason we use this property
            // instead of direct access)
            if (SelectedIndex < 0 || SelectedIndex >= Rows.Length)
                return null;

            return Rows[SelectedIndex];
        }
}

रिटर्न वैल्यू को मान्य करें
पिछले उदाहरणों में से एक में हमने सीधे Array.IndexOf()रिटर्न वैल्यू का उपयोग किया है । अगर हम जानते हैं कि यह विफल हो सकता है तो उस मामले को संभालना बेहतर होगा:

int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }

डीबग कैसे करें

मेरी राय में, इस त्रुटि के बारे में SO पर, अधिकांश प्रश्न, सरलता से टाले जा सकते हैं। जिस समय आप एक उचित प्रश्न (एक छोटे से काम के उदाहरण और एक छोटे से स्पष्टीकरण के साथ) लिखने के लिए बिताते हैं, उस समय से अधिक आसानी से आप अपने कोड को डीबग करने की आवश्यकता होगी। सबसे पहले, एरिक लिपर्ट के इस ब्लॉग पोस्ट को छोटे कार्यक्रमों के डिबगिंग के बारे में पढ़ें , मैं उनके शब्दों को यहां नहीं दोहराऊंगा लेकिन यह बिल्कुल पढ़ना चाहिए

आपके पास स्रोत कोड है, आपके पास स्टैक ट्रेस के साथ अपवाद संदेश है। वहां जाएं, राइट लाइन नंबर चुनें और आप देखेंगे:

array[index] = newValue;

आपको अपनी त्रुटि मिली, जांचें कि कैसे indexबढ़ती है। क्या यह सही है? चेक करें कि सरणी कैसे आवंटित की जाती है, कैसे indexबढ़ती है के साथ सुसंगत है ? क्या यह आपके विनिर्देशों के अनुसार सही है? यदि आप इन सभी सवालों का जवाब हां में देते हैं, तो आपको यहां StackOverflow पर अच्छी मदद मिलेगी, लेकिन कृपया पहले खुद से इसके लिए जाँच करें। आप अपना समय बचा लेंगे!

एक अच्छा शुरुआत बिंदु हमेशा अभिकर्मकों का उपयोग करना और आदानों को मान्य करना है। तुम भी कोड अनुबंध का उपयोग करना चाहते हो सकता है। जब कुछ गलत हुआ और आप यह पता नहीं लगा सकते हैं कि आपके कोड पर त्वरित नज़र के साथ क्या होता है तो आपको एक पुराने दोस्त का सहारा लेना होगा: डीबगर । विजुअल स्टूडियो (या अपने पसंदीदा आईडीई) के अंदर डिबग में अपना एप्लिकेशन चलाएं, आप देखेंगे कि वास्तव में कौन सी पंक्ति इस अपवाद को फेंकती है, कौन सी सरणी शामिल है और आप किस सूचकांक का उपयोग करने की कोशिश कर रहे हैं। वास्तव में, 99% बार आप इसे कुछ ही मिनटों में हल कर लेंगे।

यदि उत्पादन में ऐसा होता है, तो आप गलत कोड में जोर जोड़ना बेहतर समझेंगे, शायद हम आपके कोड में वह नहीं देखेंगे जो आप खुद नहीं देख सकते हैं (लेकिन आप हमेशा दांव लगा सकते हैं)।

कहानी का VB.NET पक्ष

हमने C # उत्तर में जो कुछ भी कहा है, वह स्पष्ट वाक्यविन्यास अंतर के साथ VB.NET के लिए मान्य है, लेकिन जब आप VB.NET सरणियों से निपटते हैं, तो विचार करने के लिए एक महत्वपूर्ण बिंदु है।

VB.NET में, सरणी के लिए अधिकतम वैध सूचकांक मान सेट करने के लिए ऐरे को घोषित किया जाता है। यह उन तत्वों की गिनती नहीं है जिन्हें हम सरणी में संग्रहीत करना चाहते हैं।

' declares an array with space for 5 integer 
' 4 is the maximum valid index starting from 0 to 4
Dim myArray(4) as Integer

तो यह लूप किसी भी IndexOutOfRangeException के कारण 5 पूर्णांक के साथ सरणी को भर देगा

For i As Integer = 0 To 4
    myArray(i) = i
Next

VB.NET नियम

इस अपवाद का अर्थ है कि आप किसी अमान्य अनुक्रमणिका का उपयोग करके सूचकांक द्वारा संग्रह वस्तु तक पहुँचने का प्रयास कर रहे हैं। जब यह संग्रह की निचली सीमा से कम या अधिक से अधिक होता है तो एक सूचकांक अमान्य होता हैइसमें शामिल तत्वों की संख्या के बराबर। सरणी घोषणा में परिभाषित अधिकतम अनुमत सूचकांक

20 Lijo Mar 04 2016 at 18:22

इंडेक्स ऑफ बाउंड अपवाद क्या है, इसके बारे में सरल व्याख्या:

ज़रा सोचिए कि एक ट्रेन है, जिसके डिब्बे D1, D2, D3 हैं। एक यात्री ट्रेन में प्रवेश करने के लिए आया था और उसके पास डी 4 के लिए टिकट है। अब क्या होगा। यात्री एक ऐसे डिब्बे में प्रवेश करना चाहता है जो मौजूद नहीं है इसलिए स्पष्ट रूप से समस्या उत्पन्न होगी।

समान परिदृश्य: जब भी हम किसी सरणी सूची आदि को एक्सेस करने का प्रयास करते हैं, तो हम केवल सरणी में मौजूद अनुक्रमितों तक पहुंच सकते हैं। array[0]और array[1]विद्यमान हैं। यदि हम एक्सेस करने की कोशिश करते हैं array[3], तो यह वास्तव में नहीं है, इसलिए बाध्य अपवाद से बाहर एक सूचकांक उत्पन्न होगा।

10 Snr Dec 07 2017 at 13:48

समस्या को आसानी से समझने के लिए, हमने यह कोड लिखा है:

static void Main(string[] args)
{
    string[] test = new string[3];
    test[0]= "hello1";
    test[1]= "hello2";
    test[2]= "hello3";

    for (int i = 0; i <= 3; i++)
    {
        Console.WriteLine(test[i].ToString());
    }
}

परिणाम होगा:

hello1
hello2
hello3

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.

सरणी का आकार 3 (सूचक 0, 1 और 2) है, लेकिन फॉर-लूप 4 बार (0, 1, 2 और 3) लूप करता है।
इसलिए जब यह (3) के साथ सीमा के बाहर पहुंचने की कोशिश करता है तो यह अपवाद को फेंक देता है।

1 Ricibob Mar 01 2019 at 15:51

बहुत लंबे पूर्ण स्वीकृत उत्तर से एक पक्ष IndexOutOfRangeExceptionकई अन्य अपवाद प्रकारों की तुलना में बनाने के लिए एक महत्वपूर्ण बिंदु है , और वह है:

अक्सर जटिल प्रोग्राम स्टेट होता है, जो कोड में किसी विशेष बिंदु पर नियंत्रण रखना मुश्किल होता है। उदाहरण के लिए एक DB कनेक्शन नीचे चला जाता है ताकि इनपुट के लिए डेटा पुनः प्राप्त नहीं किया जा सके आदि ... इस तरह का मुद्दा अक्सर किसी न किसी प्रकार के अपवाद में होता है। एक उच्च स्तर तक बुलबुला होना चाहिए क्योंकि जहां यह होता है उस बिंदु पर इससे निपटने का कोई तरीका नहीं है।

IndexOutOfRangeExceptionआम तौर पर यह अलग है कि ज्यादातर मामलों में यह उस बिंदु पर जांच करने के लिए बहुत मामूली है जहां अपवाद उठाया जा रहा है। आम तौर पर इस तरह के अपवाद को कुछ कोड द्वारा फेंक दिया जाता है जो उस जगह पर समस्या से बहुत आसानी से निपट सकते हैं - बस सरणी की वास्तविक लंबाई की जांच करके। आप इस अपवाद को उच्चतर संभाल कर इसे 'ठीक' नहीं करना चाहते हैं - लेकिन इसके बजाय यह सुनिश्चित करें कि पहले उदाहरण में इसे फेंका नहीं गया है - जो कि ज्यादातर मामलों में सरणी लंबाई की जांच करके करना आसान है।

इसे लगाने का एक और तरीका यह है कि इनपुट या प्रोग्राम स्टेट पर नियंत्रण की वास्तविक कमी के कारण अन्य अपवाद उत्पन्न हो सकते हैं, लेकिन IndexOutOfRangeExceptionकेवल पायलट (प्रोग्रामर) की गलती से अधिक नहीं।