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


191

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

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

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

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

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


आपके संग्रह में यदि आपके पास केवल 4 आइटम हैं, लेकिन कोड ने इंडेक्स 5 में एक आइटम प्राप्त करने की कोशिश की। यह IndexOutOfRangeException को फेंक देगा। इंडेक्स चेक करें = 5; if (items.Length> = index) Console.WriteLine (intems [index]);
बाबू कुमारसामी

जवाबों:


232

यह क्या है?

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

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

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

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क्योंकि आप डेटासेट में केवल दो कॉलम हैं, लेकिन आप 3rd वन (इंडेक्स हैं) से मान प्राप्त करने का प्रयास कर रहे हैं हमेशा 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 नियम

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


19

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

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

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


10

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

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

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

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

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.