Check if array is null or empty? - c#

I have some problem with this line of code:
if(String.IsNullOrEmpty(m_nameList[index]))
What have I done wrong?
EDIT: The m_nameList is underlined with red color in VisualStudio, and it says "the name 'm_nameList' does not exist in the current context"??
EDIT 2: I added some more code
class SeatManager
{
// Fields
private readonly int m_totNumOfSeats;
// Constructor
public SeatManager(int maxNumOfSeats)
{
m_totNumOfSeats = maxNumOfSeats;
// Create arrays for name and price
string[] m_nameList = new string[m_totNumOfSeats];
double[] m_priceList = new double[m_totNumOfSeats];
}
public int GetNumReserved()
{
int totalAmountReserved = 0;
for (int index = 0; index <= m_totNumOfSeats; index++)
{
if (String.IsNullOrEmpty(m_nameList[index]))
{
totalAmountReserved++;
}
}
return totalAmountReserved;
}
}
}

If m_nameList is null, that will still blow up, because it will try to find the element to pass to String.IsNullOrEmpty. You'd want:
if (m_nameList == null || String.IsNullOrEmpty(m_nameList[index]))
That's also assuming that index is going to be valid if m_nameList is non-null.
Of course, this is checking if the element of an array is null or empty, or if the array reference itself is null. If you just want to check the array itself (as your title suggests) you want:
if (m_nameList == null || m_nameList.Length == 0)
EDIT: Now we can see your code, there are two problems:
As Henk showed in his answer, you're trying to use a local variable when you need a field
You're also going to get an ArrayIndexOutOfBoundsException (once you've used a field) due to this:
for (int index = 0; index <= m_totNumOfSeats; index++)
That will perform m_totNumOfSeats + 1 iterations because of your bound. You want:
for (int index = 0; index < m_totNumOfSeats; index++)
Note that m_nameList[m_totNumOfSeats] is not valid, because array indexes
start at 0 in C#. So for an array of 5 elements, the valid indexes are 0, 1, 2, 3, 4.
Another option for your GetNumReserved method would be to use:
int count = 0;
foreach (string name in m_nameList)
{
if (string.IsNullOrEmpty(name))
{
count++;
}
}
return count;
Or using LINQ, it's a one-liner:
return m_nameList.Count(string.IsNullOrEmpty);
(Are you sure you haven't got it the wrong way round though? I would have thought reservations would be the ones where the name isn't null or empty, not the ones where it is null or empty.)
If it's the wrong way round, it would be this instead in LINQ:
return m_nameList.Count(name => !string.IsNullOrEmpty(name));

After Edit2:
You are defining m_nameList as a local variable of the constructor.
The rest of your code needs it as a field:
class SeatManager
{
// Fields
private readonly int m_totNumOfSeats;
private string[] m_nameList;
private double[] m_priceList;
// Constructor
public SeatManager(int maxNumOfSeats)
{
m_totNumOfSeats = maxNumOfSeats;
// Create arrays for name and price
m_nameList = new string[m_totNumOfSeats];
m_priceList = new double[m_totNumOfSeats];
}
....
}

To avoid the error you can perform some pre conditions in the if, like these :
if(m_nameList == null || index < 0 || m_nameList.Length < index || String.IsNullOrEmpty(m_nameList[index]))
This should works fine(without causing error) in almost any conditions ...

Related

How to apply arraylist with variables in an Object, inside a method using a for loop in c #

A hw was given to us to change a previous hw in C# which used 2d arrays and instead of using 2d arrays we use an Array list with variables declared in an object called Students.
I would like to use a method to calculate a student best mark; however, the method is giving me an error and a warning which are the following:
Error:
CS0161 'Form1.Calc_HighestMarkOutput(int)': not all code paths return a value.
Warning:
CS0162 Unreachable code detected.
Inside the arraylist the user inputed (through use of an overload constructor):
Student Name, Maths Mark, English Mark, Maltese Mark, Email Address.
and since in the method I am returning 3 highest marks in 3 subjects attained by all students, I decided to return an array. which will be accessed by a temporary array inside the main program by selectedindex.
Please help me find the problem.
And thanks in advance.
public int[] Calc_HighestMarkOutput(int HighestMarkIndex)
{
int[] HighestMarkOutput = new int[3];
int HighestMarkMaths = 0;
int HighestMarkEnglish = 0;
int HighestMarkMaltese = 0;
int TMPHighestMarkMaths = 0;
int TMPHighestMarkEnglish = 0;
int TMPHighestMarkMaltese = 0;
for (int i = 0; i < myStudents.Count; i++) //a loop through an array list.
{
if (myStudents[HighestMarkIndex].Maths_Result > HighestMarkMaths)
{
TMPHighestMarkMaths = myStudents[HighestMarkIndex].Maths_Result;
HighestMarkMaths = TMPHighestMarkMaths;
}
if (myStudents[HighestMarkIndex].English_Result > HighestMarkEnglish)
{
TMPHighestMarkEnglish = myStudents[HighestMarkIndex].English_Result;
HighestMarkEnglish = TMPHighestMarkEnglish;
}
if (myStudents[HighestMarkIndex].Maltese_Result > HighestMarkMaltese)
{
TMPHighestMarkMaltese = myStudents[HighestMarkIndex].Maltese_Result;
HighestMarkMaltese = TMPHighestMarkMaltese;
}
HighestMarkOutput[0] = HighestMarkMaths;
HighestMarkOutput[1] = HighestMarkEnglish;
HighestMarkOutput[2] = HighestMarkMaltese;
return HighestMarkOutput;
}
You are getting an error, because the return-statement is inside the loop. If the list is empty, the return statement will never be executed. Also, you know the result only after the loop has finished. So, place the return-statement after the loop.
Since the purpose of this method is to find the highest marks, it makes no sense to pass such an index into the routine as a parameter.
Using foreach is easier than for because you don't have to deal with indexes.
Instead of returning an array, return an unnamed student containing the results. You can drop useless temporary variables.
public Student Calc_HighestMarkOutput()
{
var result = new Student(); // You also might have to add a default constructor.
foreach (Student student in myStudents) {
if (student.Maths_Result > result.Maths_Result) {
result.Maths_Result = student.Maths_Result;
}
if (student.English_Result > result.English_Result) {
result.English_Result = student.English_Result;
}
if (student.Maltese_Result > result.Maltese_Result) {
result.Maltese_Result = student.Maltese_Result;
}
}
return result;
}
You could also use Math.Max to simplify finding the maximum value
foreach (Student student in myStudents) {
result.Maths_Result = Math.Max(result.Maths_Result, student.Maths_Result);
result.English_Result = Math.Max(result.English_Result, student.English_Result);
result.Maltese_Result = Math.Max(result.Maltese_Result, student.Maltese_Result);
}
With these refactorings, the method shrinks from 22 lines (not counting empty lines and lines containing only a brace) to 7 lines.

Unity3D ArgumentOutOfRangeException: Index was out of range

I have a problem with my code. Compiler stop on this line when I try to pick up the object.
ekwipunek.ListaNaszychPrzedmiotow[i] = BazaDanych_Eq.ListaPrzedmiotow [IdPrzedmiotu];
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
if ( Input.GetKeyDown (KeyCode.Q))
{
IdPrzedmiotu = DoPodniesienia.GetComponent<PrzedmiotPodniesienie>().id;
for (int i = 0; i < ekwipunek.ListaNaszychPrzedmiotow.Count; i++)
{
if (ekwipunek.ListaNaszychPrzedmiotow[i].id == 0 && DoPodniesienia != null)
{
ekwipunek.ListaNaszychPrzedmiotow[i] = BazaDanych_Eq.ListaPrzedmiotow [IdPrzedmiotu];
Destroy(DoPodniesienia);
DoPodniesienia = null;
}
}
}
Your problem, more than likely, exists because one of your indices on this line references something that would be outside of the range of the collection.
You're setting this variable that is used as an index to an id.
IdPrzedmiotu = DoPodniesienia.GetComponent<PrzedmiotPodniesienie>().id;
Then, you're referencing it further down without verifying that it is available in your collection.
BazaDanych_Eq.ListaPrzedmiotow [IdPrzedmiotu]
You need to validate this value or this collection before accessing it.
Future Debugging Tip: ArgumentOutOfRangeException
Check the count of any collection you are using
Check the value of any index you will use to reference the collection
public class YourClass
{
...
Debug.Log($"The collection \"ListaNaszychPrzedmiotow\" is {ListaNaszychPrzedmiotow.Count()}");
Debug.Log($"The index value of \"i\" is {i}");
...
}

search string inside string

I'm working on application that's need search method I have listbox full with items every item have singer name and song title , I need be able to search the song title or singer name on same method that's what's I tried so far :
public void search_song()
{
for (int i = listbox_titles.Items.Count - 1; i >= 0; i--)
{
int char_count = listbox_titles.Items[i].ToString().Length;
if (listbox_titles.Items[i].ToString().ToLower().Contains(txt_to_search.Text) || listbox_titles.Items[i].ToString().StartsWith(txt_to_search.Text, StringComparison.Ordinal) || listbox_titles.Items[i].ToString().ToLower().Substring(0, char_count).Contains(txt_to_search.Text)) ;
{
//listbox_titles.SetSelected(i, true);
MessageBox.Show(listbox_titles.Items[i].ToString());
}
}
its work but only search from the beginning of items not middle
any ideas ??
this example of what's I want if item is **avicii waiting for love ** if I search waiting for love is should give me the item .
You just need to find the list box item that contains what you search for, so you don't need for StartsWith method, but since you're saying that your search method only works on the starting string, I can find that you're not converting the text to lower in StartsWith as in Contains, and that might what make the issue. So if your check is case insensitive you can just use the following:
public void search_song()
{
for (int i = listbox_titles.Items.Count - 1; i >= 0; i--)
{
int char_count = listbox_titles.Items[i].ToString().Length;
if (listbox_titles.Items[i].ToString().IndexOf(txt_to_search.Text, StringComparison.OrdinalIgnoreCase) >= 0)
{
//listbox_titles.SetSelected(i, true);
MessageBox.Show(listbox_titles.Items[i].ToString());
}
}
Make sure your code isn't like your original when you do get it working because you will always get the message box:
if (listbox_titles.Items[i].ToString().ToLower().Contains(txt_to_search.Text) ||
listbox_titles.Items[i].ToString().StartsWith(txt_to_search.Text, StringComparison.Ordinal) ||
listbox_titles.Items[i].ToString().ToLower().Substring(0, char_count).Contains(txt_to_search.Text)
) ;
{
//listbox_titles.SetSelected(i, true);
MessageBox.Show(listbox_titles.Items[i].ToString());
}
That green squiggle on the syntax highlighter is pointing you to a warning that it is an empty statement - you have a semi-colon at the end of the if, so your block of code is not conditional at all.
Edit:
public void search_song(string txt_to_search)
{
foreach(var t in listbox_titles.Items)
{
String s = t.ToString().ToLower();
if(s.Contains(txt_to_search.ToLower()))
{
//listbox_titles.SetSelected(i, true);
MessageBox.Show(s);
}
}
}
This works for me because it keeps the size of the lines down to a manageable level - obviously, you would need to index using a variable rather than foreach.
Edit:
If you need to know where the occurrences are you can always define an extension helper:
public void search_song(string txt_to_search)
{
foreach (var t in listbox_titles.Items)
{
if(txt_to_search.Occurrences(t.ToString(), false).Count > 0)
MessageBox.Show(t.ToString());
}
}
}
static class StringHelpers
{
public static List<int> Occurrences(this string pattern, string source, bool caseSensitive = true)
{
List<int> occurs = new List<int>();
if (String.IsNullOrEmpty(pattern) || String.IsNullOrWhiteSpace(pattern))
return occurs;
int index = 0;
if (!caseSensitive)
{
pattern = pattern.ToLower();
source = source.ToLower();
}
while (index < source.Length) // was (index < source.Length - 1)
{
if ((index = source.IndexOf(pattern, index)) < 0)
break;
occurs.Add(index);
++index;
}
return occurs;
}
}
Just capture the list and interrogate it.
Edit: just noticed that I had the scan stop short of the end (no idea why, old age brain fade perhaps). It probably won't make a major difference unless you are searching for single characters (which is what I happened to do!)

Getting 'NullReferenceException' when putting int in Array of variety Object

I am trying to create a Sudoku in WinForms with C# as a school assignment. Everything in the sudoku MUST be object oriented so I have not chosen to structure the code like this, the teacher did.
When I put a number(int) in a Textbox in the SudokuGUI, it tries to put the number in the arrays but fails and give me the error well known:
An unhandled exception of type 'System.NullReferenceException'
occurred in WindowsFormsApplication5.exe Additional information:
Object reference not set to an instance of an object.
This is how the code look like:
First we send the Number when keyreleased from the TextBox to the method that will put the number in to the array
private void Valuechange_KeyUp(object sender, KeyEventArgs e)
{
TextBox text_box = sender as TextBox;
var position = tableLayoutPanel1.GetPositionFromControl(text_box);
int x = position.Row;
int y = position.Column;
if (int.TryParse(text_box.Text, out value) && int.Parse(text_box.Text) < 10 && int.Parse(text_box.Text) > 0 || value == 0)
{
add_value.Array_AddNumber(x, y, value);
}
else
{
MessageBox.Show("Skriv in en siffra mellan 1-9");
text_box.Clear();
}
}
Here is the method that will add the number from Textbox to the Array that will hold the numbers
class Ruta
{
Siffra number = new Siffra();
public Siffra[,] SudokuArray = new Siffra[9, 9];
public void Array_AddNumber(int x, int y, int value)
{
SudokuArray[x, y].nummer = value;
}
}
And here is the "Siffra" which means Number in Swedish, that is the the type of the Array
class Siffra
{
private int _nummer;
public int nummer
{
get { return _nummer; }
set { _nummer = value; }
}
}
What have I done wrong, I really don't understand, my teacher couldn't even help me :/
Here is the whole solution:
https://dl.dropboxusercontent.com/u/13409794/WindowsFormsApplication5.zip
The problem is a misunderstanding of this line:
public Siffra[,] SudokuArray = new Siffra[9,9];
That line creates a new 2-dimensional array object in memory, with space for 9 items x 9 items (81 in total). The misunderstanding is that the contents of each spot in the array is still null. Therefore, later on in your code, when you do this:
SudokuArray[x,y].nummer = value;
The code first looks up the array reference and uses that to find the element at position (x,y). That value is still null. The code then ties to use the nummer property of a null reference. Oops. You can't do that.
To fix it, you need to add this code to the constructor for your Ruta class:
for (int x = 0; x < 9; x++)
for (int y = 0; y < 9; y++)
SudokuArray[x,y] = new Siffra();
Since SudukuArray isn't null, the problem (the null value) must be the thing in it.
Siffra is a class - a reference type. That means instances of it are null by default (unlike structs, or value types).
So when you create a 9x9 array of them, you are creating a 9x9 array of nulls.
The rest is homework.
You are initializing the array:
public Siffra[,] array = new Siffra[9,9];
But never creating individual Siffra instances. Therefore, when you attempt to access one, you are actually getting a null. You then attemp to get a nummer from the null instance... which leads to the exception.
Solution
Initialize each instance in the array before you use it:
for(int i=0; i<9; i++)
for(int j=0; j<9; j++)
array[i,j] = new Siffra();
You have allocated the array to have a size that can hosts 9x9 Siffra, and that is right, but the 81 slots present in the array are all NULL.
None contains a Siffra so, when your code executes
SudokyArray[x,y].nummer = value;
it is like you are writing
null.nummer = value;
of course this is a NullReferenceException
Somewhere, possibly in the constructor of your class Ruta you need to fill the array with 81 instances of the class Siffra
class Ruta
{
public Siffra[,] SudokyArray;
public Ruta()
{
SudokyArray = new Sufra[9,9]
for(int i = 0; i < 9; i++)
{
for(int y = 0; y < 9; y++)
SudokuArray[i, y] = new Suffra();
}
}
}

How can I find the last element in a List<>?

The following is an extract from my code:
public class AllIntegerIDs
{
public AllIntegerIDs()
{
m_MessageID = 0;
m_MessageType = 0;
m_ClassID = 0;
m_CategoryID = 0;
m_MessageText = null;
}
~AllIntegerIDs()
{
}
public void SetIntegerValues (int messageID, int messagetype,
int classID, int categoryID)
{
this.m_MessageID = messageID;
this.m_MessageType = messagetype;
this.m_ClassID = classID;
this.m_CategoryID = categoryID;
}
public string m_MessageText;
public int m_MessageID;
public int m_MessageType;
public int m_ClassID;
public int m_CategoryID;
}
I am trying to use the following in my main() function code:
List<AllIntegerIDs> integerList = new List<AllIntegerIDs>();
/* some code here that is ised for following assignments*/
{
integerList.Add(new AllIntegerIDs());
index++;
integerList[index].m_MessageID = (int)IntegerIDsSubstring[IntOffset];
integerList[index].m_MessageType = (int)IntegerIDsSubstring[IntOffset + 1];
integerList[index].m_ClassID = (int)IntegerIDsSubstring[IntOffset + 2];
integerList[index].m_CategoryID = (int)IntegerIDsSubstring[IntOffset + 3];
integerList[index].m_MessageText = MessageTextSubstring;
}
Problem is here: I am trying to print all elements in my List using a for loop:
for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++) //<----PROBLEM HERE
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n", integerList[cnt3].m_MessageID,integerList[cnt3].m_MessageType,integerList[cnt3].m_ClassID,integerList[cnt3].m_CategoryID, integerList[cnt3].m_MessageText);
}
I want to find the last element so that I equate cnt3 in my for loop and print out all entries in the List. Each element in the list is an object of the class AllIntegerIDs as mentioned above in the code sample. How do I find the last valid entry in the List?
Should I use something like integerList.Find(integerList[].m_MessageText == null;?
If I use that it will need an index that will range from 0 to whatever maximum. Means I will have to use another for loop which I do not intend to use. Is there a shorter/better way?
To get the last item of a collection use LastOrDefault() and Last() extension methods
var lastItem = integerList.LastOrDefault();
OR
var lastItem = integerList.Last();
Remeber to add using System.Linq;, or this method won't be available.
If you just want to access the last item in the list you can do
if (integerList.Count > 0)
{
// pre C#8.0 : var item = integerList[integerList.Count - 1];
// C#8.0 :
var item = integerList[^1];
}
to get the total number of items in the list you can use the Count property
var itemCount = integerList.Count;
In C# 8.0 you can get the last item with ^ operator full explanation
List<char> list = ...;
var value = list[^1];
// Gets translated to
var value = list[list.Count - 1];
Lets get at the root of the question, how to address the last element of a List safely...
Assuming
List<string> myList = new List<string>();
Then
//NOT safe on an empty list!
string myString = myList[myList.Count -1];
//equivalent to the above line when Count is 0, bad index
string otherString = myList[-1];
"count-1" is a bad habit unless you first guarantee the list is not empty.
There is not a convenient way around checking for the empty list except to do it.
The shortest way I can think of is
string myString = (myList.Count != 0) ? myList [ myList.Count-1 ] : "";
you could go all out and make a delegate that always returns true, and pass it to FindLast, which will return the last value (or default constructed valye if the list is empty). This function starts at the end of the list so will be Big O(1) or constant time, despite the method normally being O(n).
//somewhere in your codebase, a strange delegate is defined
private static bool alwaysTrue(string in)
{
return true;
}
//Wherever you are working with the list
string myString = myList.FindLast(alwaysTrue);
The FindLast method is ugly if you count the delegate part, but it only needs to be declared one place. If the list is empty, it will return a default constructed value of the list type "" for string. Taking the alwaysTrue delegate a step further, making it a template instead of string type, would be more useful.
int lastInt = integerList[integerList.Count-1];
Though this was posted 11 years ago, I'm sure the right number of answers is one more than there are!
You can also doing something like;
if (integerList.Count > 0)
var item = integerList[^1];
See the tutorial post on the MS C# docs here from a few months back.
I would personally still stick with LastOrDefault() / Last() but thought I'd share this.
EDIT;
Just realised another answer has mentioned this with another doc link.
Change
for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++)
to
for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
Use the Count property. The last index will be Count - 1.
for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
Why not just use the Count property on the List?
for(int cnt3 = 0; cnt3 < integerList.Count; cnt3++)
Independent of your original question, you will get better performance if you capture references to local variables rather than index into your list multiple times:
AllIntegerIDs ids = new AllIntegerIDs();
ids.m_MessageID = (int)IntegerIDsSubstring[IntOffset];
ids.m_MessageType = (int)IntegerIDsSubstring[IntOffset + 1];
ids.m_ClassID = (int)IntegerIDsSubstring[IntOffset + 2];
ids.m_CategoryID = (int)IntegerIDsSubstring[IntOffset + 3];
ids.m_MessageText = MessageTextSubstring;
integerList.Add(ids);
And in your for loop:
for (int cnt3 = 0 ; cnt3 < integerList.Count ; cnt3++) //<----PROBLEM HERE
{
AllIntegerIDs ids = integerList[cnt3];
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n",
ids.m_MessageID,ids.m_MessageType,ids.m_ClassID,ids.m_CategoryID, ids.m_MessageText);
}
I would have to agree a foreach would be a lot easier something like
foreach(AllIntegerIDs allIntegerIDs in integerList)
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n", allIntegerIDs.m_MessageID,
allIntegerIDs.m_MessageType,
allIntegerIDs.m_ClassID,
allIntegerIDs.m_CategoryID,
allIntegerIDs.m_MessageText);
}
Also I would suggest you add properties to access your information instead of public fields, depending on your .net version you can add it like public int MessageType {get; set;} and get rid of the m_ from your public fields, properties etc as it shouldnt be there.

Categories

Resources