I get a System.IndexOutOfRangeException [duplicate] - c#

This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 4 years ago.
I am having a issue with my current code. I am doing a project like a lot of other people, the famous SodaCrate project.
I do NOT, want any solutions to the code, but I DO want to know why I can't solve the issue with "System.IndexOutOfRangeException".
I get this error when I try to add more than 24 bottles into my crate (I apologize for the swedish comments).
Here is my code:
public void add_soda()
{
Console.WriteLine("\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"");
Console.WriteLine("| Choose your beverage! |");
Console.WriteLine("| |"); // Välkomnar användaren
Console.WriteLine("|[1] Pepsi , Soda, 11kr |");
Console.WriteLine("|[2] Coca-Cola , Soda, 12kr |");
Console.WriteLine("|[3] Coors Light , Beer, 18kr |");
Console.WriteLine("|[4] Fiji, Water , 13kr |");
Console.WriteLine("|[5] Nocco , Energy drink , 22kr |");
Console.WriteLine("|[6] Redbull , Energy drink , 25kr |");
Console.WriteLine("| |");
Console.WriteLine("\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"");
//Console.WriteLine("[7] Randomize"); <---------- Fixa
int temp = 0;
while (!int.TryParse(Console.ReadLine(), out temp) || !(temp < 7 && temp > 0)) // Detta är en failsafe, ifall väljaren väljer något som är över 7 eller under 1 kommer följande kod att skrivas ut.
{
// Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid input, please try again.");
// Console.ResetColor();
}
switch (temp) // Denna switch statementen kommer låta oss lägga till flaskor i vår crate
{
case 1: //Om anändaren skriver in ett tal mellan 1 och 6, kommer följade cases att skriva ut deras WriteLines.
Console.WriteLine(">>> You choose Pepsi <<<");
myCrate[numberOfBottles] = new Bottle("Pepsi", "Soda", 11); // Här skapas en läsk med namnet Pepsi som kostar 11kr.
numberOfBottles++; // Denna funktion gör så att en läsk läggs in i craten varje gång den skapas.
break;
case 2:
Console.WriteLine(">>> You choose Coca-Cola <<<");
myCrate[numberOfBottles] = new Bottle("Coca-Cola", "Soda", 12);
numberOfBottles++;
break;
case 3:
Console.WriteLine(">>> You choose Coors Light <<<");
myCrate[numberOfBottles] = new Bottle("Coors Light", "Beer", 18);
numberOfBottles++;
break;
case 4:
Console.WriteLine(">>> You choose Fiji <<<");
myCrate[numberOfBottles] = new Bottle("Fiji", "Water", 13);
numberOfBottles++;
break;
case 5:
Console.WriteLine(">>> You choose Nocco <<<");
myCrate[numberOfBottles] = new Bottle("Nocco", "Energydrink", 22);
numberOfBottles++;
break;
case 6:
Console.WriteLine(">>> You choose Redbull <<<");
myCrate[numberOfBottles] = new Bottle("Redbull", "Energydrink", 25);
numberOfBottles++;
break;
default:
//Console.ForegroundColor = ConsoleColor.Red; // <<<<<----------------- FIXA
Console.WriteLine("Invalid choice!");
// Console.ResetColor;
break;
}
try
{
if(numberOfBottles >= 25)
{
Console.WriteLine("The crate is currently full!");
}
}
catch (IndexOutOfRangeException e)
{
Console.WriteLine(e.Message);
throw new ArgumentOutOfRangeException("index paramater is out of range", e);
}
}
Could anyone give me an idea or a hint of why the Exception is being thrown?
I've tried a if-else statement as well I am currently quite confused.

The issue you have occurring is due to your attempt to access a part of the Bottle[] that doesn't exist. Since you've instantiated the array with a certain length, you cannot go beyond that length. Initially (and always) you should check the index you wish to use against the Length property of your array prior to attempting to access it. Say we have an int[] for this entire example, and we initially give it a Length of 3.
int[] someNumbers = new int[3];
In the case above we have created a new int array with 3 slots of memory allocated. If we try to access it with hard coded numbers the problem becomes very obvious:
someNumbers[0] = 1;
someNumbers[1] = 2;
someNumbers[2] = 3;
someNumbers[3] = 4; // Blows up here.
The reason the fourth index of 3 doesn't work is because in C# collections are accessed with zero-based indices; this means that access starts at zero and climbs from there. So technically elements are one space behind where you think they are (if you're used to traditional number systems that start at one).
If you take the same principal and access the array with a variable instead, the same thing will happen but it is less obvious; I assume you know what the ++ operator does for this.
int index = 0;
int[] someNumbers = new int[3];
someNumbers[index++] = 1; // index = 0
someNumbers[index++] = 2; // index = 1
someNumbers[index++] = 3; // index = 2
someNumbers[index++] = 4; // index = 3 :: Blows up here.
This blows up on the fourth index of 3 for the exact same reason. The way to get around this is to check the index prior to access:
if (index < someNumbers.Length)
someNumbers[index++] = 1;
This code works because the assignment will only execute if the index is within the bounds of the array.
Adjust Array Size
Now in other languages there are ways to resize an array quite easily; but C# doesn't let us do this. Good new for us, there is a work-around for it. Again I will be using int[] to demonstrate.
Say we create our initial array with a length of 3:
int[] someNumbers = new int[3];
Somewhere along the road, we decide we need more than 3 numbers for some reason; well, we need a bigger array now. To do this (very inefficiently) you can create a new array with a bigger size and just add all of the old values:
int[] newNumberArray = new int[someNumbers.Length + 10];
for (int i = 0; i < someNumbers.Length; i++)
newNumberArray[i] = someNumbers[i];
The code above creates a bigger array, and puts all of your old values in it. Remember that this is a very inefficient way to accomplish this and I heavily recommend using List<T> or some similar object instead.

In My opinion the problem is, you use the index operator on myCrate [] on array, which does not have enough space in it.
If you know you will have 7 item in the array then please create enough place for it. This code should be like this:
myCrate = new Bottle[7];
And now youa are able to use the index operator for 0 to 6 on this array

Related

C# guessing game only one guess

i´m stuck with my guessing game where i should only have one chance to guess and then get a "the end" line. I have tried to make a break and breakLoop commands but i´t didn´t work and i have no idé how to fick it... Can someone please help me?
here is my code, i know that it´s not optimal and it´s because i´m still learning C#.
Console.WriteLine("gissa talet\nDu ska nu gissa ett tal mellan 1 ocn 100, så
varsågod..\nskriv in ett tal");
var str = Console.ReadLine();
int guess = Convert.ToInt32(str);
//gonna make it random between 1-100
Random rd = new Random();
int rand_num = rd.Next(1, 10);
{
//when i guess right
if (guess == rand_num)
{
Console.WriteLine("Ditt Tal är rätt. grattis!");
}
//when it´s to small guess
else if (guess < rand_num)
{
Console.WriteLine("Ditt tal är för litet. gissa på ett större tal");
}
//when i guess to big
else if (guess > rand_num)
{
Console.WriteLine("Ditt tal är för stort. gissa på ett mindre tal");
}
// when i was close to the answer
else if (Math.Abs(guess - rand_num) <= 3)
{
Console.WriteLine("Du är dock nära och det bränns");
}
// when i guess a number thats over the number i chould guess
else (guess > 10)
{
Console.WriteLine("Du måste skriva in ett tal mellan 1 och 100!");
}
}
Edit:
As you indeed only want one guess to be allowed, there is no loop needed and your code should work fine (apart form an if missing after the last else statement). To have a "game ended" notice at the end, just add
Console.WriteLine("Game ended.");
at the end of your code.
Also, there is one pair of curled brackets that is not needed in your code. The opening one after int rand_num = rd.Next(1, 10); and the closing one at the very end of the code.
And here remains the original answer:
At first, you want a loop around parts of your code to repeat as long as the answer was not correct or there are no more tries left (in case tries are limited):
const int tries = 10; // Give the player 10 tries to guess correctly.
for (int i = 0; i < tries; i++)
{
if (guessedCorrectly)
{
break;
}
}
Second, you want to define the random number before starting the loop and you want to query new guesses from the player inside the loop:
//Set random number.
//Define Loop.
{
//Get new guess from player.
//Check if guess was correct.
//Decide weather the loop must continue or ended.
}
//Display some result.
Third, you need to change the order of your if else statements. In your code, the statement inside the if (Math.Abs(guess - rand_num) <= 3) guard can never be executed, because you check for guess < rand_num and guess > rand_num before. And since you use else if, the following conditions will never be evaluated if the ones before were true. When using if else statements, alsways check for the more specific cases before checking for the more broad cases. But in your case you probably would want to just remove the else, so both conditions (and both texts) can be executed.

Why does int seem to default to 50 rather than 0 in C#?

My goal here is to ask the user to which of the three pre-determined ascii art picture they would like to print and how many times they would like to print it. My problem is that it print about 50 times more than whichever number was chosen. I tried converting the print variable, but that didn't work. I'm fairly new to C# so I apologize for any major, basic errors.
Console.WriteLine("Would you like to print picture 1, 2, or 3?");
int print = 0;
string choice = "";
while (choice != "end")
{
choice = Console.ReadLine();
switch (choice)
{
case "1":
Console.WriteLine("How many times would you like to print it");
print = Convert.ToInt32(Console.ReadLine());
while (print > 10)
{
Console.WriteLine(cocaCola);
print -= 1;
}
break;
case "2":
Console.WriteLine("How many times would you like to print it");
print = Convert.ToInt32(Console.ReadLine());
while (print > 10)
{
Console.WriteLine(beam);
print -= 1;
}
break;
case "3":
Console.WriteLine("How many times would you like to print it");
print = Convert.ToInt32(Console.ReadLine());
while (print > 10)
{
Console.WriteLine(liberty);
print -= 1;
}
break;
default:
Console.WriteLine("You chose nothing");
break;
}
Console.WriteLine("Choose again, or type \"end\" to exit");
It's not printing 50 more times; it's printing exactly 48 more times.
You're reading a character and assigning its unicode value to an integer. For example, user types '1', the character 1. Its Unicode value is 49. You assign 49 to your int print, and there you are. A character is really a small or smallish integer, which is essentially an index into a table of characters somewhere.
In Unicode, as in ASCII, '0' is decimal 48, '1' is decimal 49, and so on in order up to '9'. That's where the 48 is coming from.
What you want to do is something more like this. First, you want to read the whole line, not just the first character; what if the user types "12"? Then you need parse the string "1" (or "12", which is two characters) to get the integer '1' or '12'.
And in fact, that's just what you did on this line:
print = Convert.ToInt32(Console.ReadLine());
So use that version every place that you've got print = Console.Read();
Second bug: You set print to some presumably small number, say the user types "4" so it's 4. Then you loop while it's greater than 10 -- but it's never greater than 10. You want to loop while it's greater than zero:
while (print > 0)
{
Console.WriteLine(cocaCola);
print -= 1;
}
You need to fix that in three places.
Update
Three places is more than you want to deal with. So here's another thing: You could simplify this code quite a bit by just setting a variable in the switch statement, and writing the loop only once (you could simplify it further in other ways, but let's take one step at a time):
Console.WriteLine("Would you like to print picture 1, 2, or 3?");
int print = 0;
string choice = "";
while (choice != "end")
{
choice = Console.ReadLine().Trim();
String thingToPrint = null;
switch (choice)
{
case "1":
thingToPrint = cocaCola;
break;
case "2":
thingToPrint = beam;
break;
case "3":
thingToPrint = liberty;
break;
}
if (thingToPrint != null)
{
Console.WriteLine("How many times would you like to print it");
print = Convert.ToInt32(Console.ReadLine());
while (print > 0)
{
Console.WriteLine(thingToPrint);
print -= 1;
}
}
else
{
Console.WriteLine("You chose poorly. Try again.");
}
Console.WriteLine("Choose again, or type \"end\" to exit");
}

difference between instantiating before the for loop and instantiating in the for loop [duplicate]

This question already has answers here:
Random number generator only generating one random number
(15 answers)
Closed 7 years ago.
so I've just started learning C# and I got across this exercise that rolls a die N times and then print the number of times each side is rolled. I got the answer however, my question lies on instantiating a Random number object before the loop and in the loop.
my code goes like this (this is instantiation before the loop):
static void DiceRoll(int RollTimes)
{
int roll = 0;
int counter1 = 0;
int counter2 = 0;
int counter3 = 0;
int counter4 = 0;
int counter5 = 0;
int counter6 = 0;
Random rnd = new Random();
for (int ctr = 0; ctr < RollTimes; ctr++)
{
roll = 0;
roll = (int)rnd.Next(1, 7);
switch (roll)
{
case 1:
counter1++;
break;
case 2:
counter2++;
break;
case 3:
counter3++;
break;
case 4:
counter4++;
break;
case 5:
counter5++;
break;
case 6:
counter6++;
break;
}
}
Console.WriteLine("1 is rolled {0} times.", counter1);
Console.WriteLine("2 is rolled {0} times.", counter2);
Console.WriteLine("3 is rolled {0} times.", counter3);
Console.WriteLine("4 is rolled {0} times.", counter4);
Console.WriteLine("5 is rolled {0} times.", counter5);
Console.WriteLine("6 is rolled {0} times.", counter6);
}
and the result is like this:
1 is rolled A times.
2 is rolled B times.
3 is rolled C times.
4 is rolled D times.
5 is rolled E times.
6 is rolled F times.
before I got that right, the instantiation line (Random rnd = new Random();) was in the loop.
for (int ctr = 0; ctr < RollTimes; ctr++)
{
roll = 0;
roll = (int)rnd.Next(1, 7);
Random rnd = new Random();
// rest of the code
then the result (at random):
1 is rolled (N) times.
2 is rolled (N) times.
3 is rolled (N) times.
4 is rolled (N) times.
5 is rolled (N) times.
6 is rolled (N) times.
Can somebody explain or enlighten me why the position of instantiation alters the result? Thanks!
The ideea is that the random object is not actually "random" and to be honest there is no such a thing in reality, except maybe in quantum mechanics. Ok, going back to your problem, to enlight you the ideea is that behind the scenes there is a series ( very complex one ) that for a given starting value ( the default value is zero) next values are computer by grabbing internally the next series value. The complexity of the internal series gives you the feeling that values are actually random. Whenever you instantiate a new Random object with the same seed you will have the same series values. The best pattern would be to use the overloaded constructor that takes a seed and use for the seed value something lime Datetime.Now.Ticks and try to cache your random object.
So the answer is to create a single instance outside the loop and even better use a different seed for each instantiation.

Variable in switch, switch in loop, error in case 2

Welcome, I've a problem with small funcion in switch.
My problem is "Use of unassigned local variable 'matrix'"
Here is a code:
static void Main(string[] args)
{
char wyj = 'n';
do
{
Console.WriteLine("1. add numbers into matrix \n2. show matrix \n3. end");
int a;
Console.Write("\nYour choice: ");
a = int.Parse(Console.ReadLine());
switch (a)
{
case 1:
Console.WriteLine("You choose: 1");
int element;
Console.Write("\nsize of matrix: ");
int matrixsize;
matrixsize = Int32.Parse(Console.ReadLine());
int[,] matrix = new int[matrixsize, matrixsize];
for (int i = 0; i <= matrixsize - 1; i++)
{
for (int j = 0; j <= matrixsize - 1; j++)
{
Console.Write("element{0},{1} =", i + 1, j + 1);
element = int.Parse(Console.ReadLine());
matrix[i, j] = element;
}
}
break;
case 2:
Console.WriteLine("You choose 2");
foreach (int x in matrix)
Console.Write(x);
break;
case 3:
Console.WriteLine("End the program? y- yes, n- no");
wyj = char.Parse(Console.ReadLine());
break;
}
}
while (wyj != 'y');
Console.WriteLine("Koniec programu!");
Console.ReadKey();
}
What i need to do?
After Doc Brown answer, in case 2 nothing happens, the matrix is empty.
I think the loop is the problem?
You should not assume that the user first enters 1, then 2, but expect that this might happen the other way round.
the declaration int[,] matrix must be done before the switch statement, and you should set the variable to null there int[,] matrix=null;
the initialization matrix = new int[matrixsize, matrixsize] can stay where it is, but
in the case 2 block, you have to check if matrix was initialized if(matrix!=null) {/*...*/}
You've made an assumption that the compiler can't verify--that you will always generate the matrix be forming viewing it. The compiler knows that this doesn't have to be the case in a switch statement, so it prevents you from using a variable which may never have been set (or in this case, even declared). If you want to keep this code, declare the variable outside of the case and initialize it to a new matrix. Then check in case two if it is safe to display.
The matrix variable is local to the switch. The case 2 uses the variable from case 1, because case does not introduce a scope, but it is never initialized there, because the initializer is not executed when doing case 2.
Moving the variable out of the switch will silence the error if you provide initializer, but it will still be a new variable in each iteration, so when you fill it in in case 1, it will come out empty when doing case 2 in next iteration. You need to move the variable all the way out of the loop to have to values persist.
You still should not assume that user provides the inputs in correct order, so in the case 2 you have to check that the matrix was already filled in.

Problems with loop

I'm still in the learning stages of C# and I need some help with my program.
My program is supposed to work like this, I have 6 salesmen, and in the console I will write information about them(name, sold items etc). How can I make it loop 6 times (one for each salesmen)?
for (int i = 0; i < 6; i++) (Couldn't come up with more than this)
{
salesmen[] seller = new salesmen[6];
//Salesmen name
Console.WriteLine("Enter name: ");
salesmen[0].namn = System.Console.ReadLine();
//Birth certificate
Console.WriteLine("Enter birth certificate: ");
salesmen[0].birthvertificate = Console.ReadLine();
//Enter district
Console.WriteLine("Enter district: ");
salesmen[0].district = Console.ReadLine();
//Enter solditems
Console.WriteLine("Enter solditems: ");
salesmen[0].solditems = int.Parse(Console.ReadLine());
//Calculates what level each salesmen has reached
if (salesmen[0].solditems < 50)
salesmen[0].level = 1;
if (salesmen[0].solditems >= 50 && salesmen[0].solitems < 99)
salesmen[0].level = 2;
if (salesmen[0].solitems >= 100 && salesmen[0].solditems < 199)
salesmen[0].level = 3;
if (salesmen[0].solditems > 199)
salesmen[0].level = 4;
Is the code you provided the full code you are using? After quickly glancing at it, I noticed that your array declaration is inside the for loop, initializing the array 6 times with an empty one.
And as #Drenguin stated, you should use an index (in this case i) to modify the right salesman.
I also noticed that you wrote
salesmen[0].namn
instead of
salesmen[0].name
and although your code works, the correct spelling might be more readable.
You are only changing the salesmen[0]. In order to use the loop replace all salesmen[0] with salesmen[i].

Categories

Resources