IndexOutOfRangeException / ArgumentOutOfRangeException क्या है और मैं इसे कैसे ठीक करूं?
मेरे पास कुछ कोड हैं और जब यह निष्पादित होता है, तो यह ए IndexOutOfRangeException
, कहती है,
अनुसूची, सारणी की सीमाओं से बाहर थी।
इसका क्या मतलब है, और मैं इसके बारे में क्या कर सकता हूं?
उपयोग की जाने वाली कक्षाओं के आधार पर यह भी हो सकता है ArgumentOutOfRangeException
Mscorlib.dll में 'System.ArgumentOutOfRangeException' प्रकार का एक अपवाद हुआ, लेकिन उपयोगकर्ता कोड में संभाला नहीं गया था अतिरिक्त जानकारी: सूचकांक सीमा से बाहर था। गैर-नकारात्मक और संग्रह के आकार से कम होना चाहिए।
जवाब
यह क्या है?
इस अपवाद का अर्थ है कि आप किसी अमान्य अनुक्रमणिका का उपयोग करके सूचकांक द्वारा संग्रह वस्तु तक पहुँचने का प्रयास कर रहे हैं। जब यह संग्रह की निचली सीमा से कम या इसके तत्वों की संख्या के बराबर या उससे अधिक होता है, तो एक इंडेक्स अमान्य होता है।
जब यह फेंक दिया जाता है
दिए गए एक सरणी को देखते हुए:
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.Arra
y, इसके लिए लागू नहीं होता 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 नियम
इस अपवाद का अर्थ है कि आप किसी अमान्य अनुक्रमणिका का उपयोग करके सूचकांक द्वारा संग्रह वस्तु तक पहुँचने का प्रयास कर रहे हैं। जब यह संग्रह की निचली सीमा से कम या अधिक से अधिक होता है तो एक सूचकांक अमान्य होता हैइसमें शामिल तत्वों की संख्या के बराबर। सरणी घोषणा में परिभाषित अधिकतम अनुमत सूचकांक
इंडेक्स ऑफ बाउंड अपवाद क्या है, इसके बारे में सरल व्याख्या:
ज़रा सोचिए कि एक ट्रेन है, जिसके डिब्बे D1, D2, D3 हैं। एक यात्री ट्रेन में प्रवेश करने के लिए आया था और उसके पास डी 4 के लिए टिकट है। अब क्या होगा। यात्री एक ऐसे डिब्बे में प्रवेश करना चाहता है जो मौजूद नहीं है इसलिए स्पष्ट रूप से समस्या उत्पन्न होगी।
समान परिदृश्य: जब भी हम किसी सरणी सूची आदि को एक्सेस करने का प्रयास करते हैं, तो हम केवल सरणी में मौजूद अनुक्रमितों तक पहुंच सकते हैं। array[0]
और array[1]
विद्यमान हैं। यदि हम एक्सेस करने की कोशिश करते हैं array[3]
, तो यह वास्तव में नहीं है, इसलिए बाध्य अपवाद से बाहर एक सूचकांक उत्पन्न होगा।
समस्या को आसानी से समझने के लिए, हमने यह कोड लिखा है:
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) के साथ सीमा के बाहर पहुंचने की कोशिश करता है तो यह अपवाद को फेंक देता है।
बहुत लंबे पूर्ण स्वीकृत उत्तर से एक पक्ष IndexOutOfRangeException
कई अन्य अपवाद प्रकारों की तुलना में बनाने के लिए एक महत्वपूर्ण बिंदु है , और वह है:
अक्सर जटिल प्रोग्राम स्टेट होता है, जो कोड में किसी विशेष बिंदु पर नियंत्रण रखना मुश्किल होता है। उदाहरण के लिए एक DB कनेक्शन नीचे चला जाता है ताकि इनपुट के लिए डेटा पुनः प्राप्त नहीं किया जा सके आदि ... इस तरह का मुद्दा अक्सर किसी न किसी प्रकार के अपवाद में होता है। एक उच्च स्तर तक बुलबुला होना चाहिए क्योंकि जहां यह होता है उस बिंदु पर इससे निपटने का कोई तरीका नहीं है।
IndexOutOfRangeException
आम तौर पर यह अलग है कि ज्यादातर मामलों में यह उस बिंदु पर जांच करने के लिए बहुत मामूली है जहां अपवाद उठाया जा रहा है। आम तौर पर इस तरह के अपवाद को कुछ कोड द्वारा फेंक दिया जाता है जो उस जगह पर समस्या से बहुत आसानी से निपट सकते हैं - बस सरणी की वास्तविक लंबाई की जांच करके। आप इस अपवाद को उच्चतर संभाल कर इसे 'ठीक' नहीं करना चाहते हैं - लेकिन इसके बजाय यह सुनिश्चित करें कि पहले उदाहरण में इसे फेंका नहीं गया है - जो कि ज्यादातर मामलों में सरणी लंबाई की जांच करके करना आसान है।
इसे लगाने का एक और तरीका यह है कि इनपुट या प्रोग्राम स्टेट पर नियंत्रण की वास्तविक कमी के कारण अन्य अपवाद उत्पन्न हो सकते हैं, लेकिन IndexOutOfRangeException
केवल पायलट (प्रोग्रामर) की गलती से अधिक नहीं।