printing objects with a foreach loop and NullReferenceException [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 8 years ago.
I've started learning C# and I have been following a few "mini projects" I found on the net and some I made up my self to help me understand the basics. This mini project requires me to create two classes that are named "item" and "inventory". The idea is that the item class is used to create items and the other inventory class is used to store the items and print them all. Here's the code so far:
class Program
{
static void Main(string[] args)
{
inventory my_inventory = new inventory();
item cake = new item("Cake", 2.99, 001);
item carrot = new item("Carrot", 0.59, 002);
my_inventory.add_item(cake);
my_inventory.add_item(carrot);
my_inventory.print_inv();
Console.ReadLine();
}
}
class item
{
string name;
double price;
int id;
public item (string Name, double Price, int ID)
{
this.name = Name;
this.price = Price;
this.id = ID;
}
public item()
{
this.name = "unknown";
this.price = 0.00;
this.id = 000;
}
public override string ToString()
{
return "Name: " + name + " Price: " + price + " ID Number: " + id;
}
}
class inventory
{
object[] inv_list = new object[10];
int tracker = 0;
public void add_item(object obj)
{
inv_list[tracker] = obj;
tracker++;
}
public void print_inv()
{
foreach ( object obj in inv_list) { Console.WriteLine(obj.ToString()); }
}
}
The error I keep running into is the "NullReferenceException" inside the print_inv() method and from what I have read it means that the object I'm trying to use on the print_inv() method is null? I'm not sure what this means in my code.

The thing here is that when you create an array of something it's initialized with the default value for something. In case of object the default value is null.
So you need to modify you print_inv method to look through existing items:
public void print_inv()
{
for(int i =0; i < tracker; i++)
{
Console.WriteLine(inv_list[i].ToString());
}
}

The issue is that since your declaring an array of a specific size (new object[10]) th earray is always that size. Therefore, when you iterate over it (foreach(object obj in inv_list) you're going to get everything, not just the values you've explicitly initialized. Since the default of object is null, then all but those explicit items out of your array are null.
There are a couple ways to fix this:
Replace foreach with for(int i = 0; i < tracker; i++) - this will only iterate through the items up to the tracker count, and no more.
Use a List<object> instead of an array. This will allow you to add/remove items without having to worry about capacity explicitly, and thus should avoid most auto-initialized values. May require more code to keep the inventory under 10 items, though.
Check for null and continue or break when you hit a null item in the inventory.

Related

Does expanding arraylist of objects make a new object?

Assume we have an array list of type Employe , does expanding it's length by 1 make a new object in the list ?
is the code in else statement correct? and is it recommended?
public void ModifierEmp(int c)
{
for(int i = 0; i < Ann.Count; i++)
{
if(Ann[i].Code == c)
{
Ann[i].saisie();
} else
{
i = Ann.Count + 1; //expanding arraylist ann
Ann[i].saisie(); //saisie a method for the user to input Employe infos
}
}
}
https://imgur.com/VfFHDKu "code snippet"
i = Ann.Count + 1;
The code above is not expanding the list: it is only setting your index variable (i) to have a new value.
If you wanted to make the list bigger, you would have to tell it which object to put into that new space you create. For example:
Ann.Add(anotherItem);
Of course, this gives you the ability to decide whether to add an existing item, create a new item (e.g. Ann.Add(new Something() { Code = c })), or even add a null value to the list (which is not usually a good idea).

c#, Get index of current T in List<T>

I searched quite a bit, i might have Stumbled upon something simmilar but it's stil unlear to me as im new to c#.
i want to get current index of the object Tline from a List< Tline >
i know i can simply iterate with an int i. but i dont understand why or how i can get it as indexof current iteam i'm possitioned on without searching for anything.
Tline is something like
public class Tline
{
public string Cd_m { get; set; }
public string cd_d { get; set; }
public string cd_c { get; set; }
...
}
My Issue is here(indicated with arrows)
class ACCS
{
internal void DBwrite(List<Tline> imoprtati)
{
OleDbConnection myAccessConn = null;
string sFld = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string dbName = "EDIM1.mdb";
dbName = Path.Combine(sFld, dbName);
string accessConn = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}", dbName);
string sqlQuery = "INSERT INTO DOTes (`id`,`cd_m`,`cd_d`,`cd_c`,`nume`) values (?,?,?,?,?)";
myAccessConn = new OleDbConnection(accessConn);
int i = 2;
foreach (var insT in imoprtati)
{
using (OleDbCommand cmd = new OleDbCommand(sqlQuery, myAccessConn))
{
cmd.Connection.Open();
cmd.Parameters.AddWithValue("#id", i.ToString());
cmd.Parameters.AddWithValue("#cd_m", "2018");
cmd.Parameters.AddWithValue("#cd_d", "BO");
cmd.Parameters.AddWithValue("#cd_c", "C00128");
// Show current Tline Index ↓↓↓↓↓↓↓
cmd.Parameters.AddWithValue("#nume", (imoprtati.GetEnumerator().Current)); //trying anything
cmd.ExecuteNonQuery();
cmd.Connection.Close();
i++;
}
}
}
}
imoprtati.GetEnumerator().Current won't work, because you are creating a separate iterator with it's own position (before the first item currently). Your best bet here is probably a simple counter:
int index = 0;
foreach (var insT in imoprtati) {
// your stuff here...
index++;
}
There is a LINQ method that includes the index, but that will end up being less efficient due to custom struct iterators, captured variables, etc (complex topic).
List.GetEnumerator().Current will create a new enumerator for the list and then return the current item, not index. This will not work as you have seen.
As List implements IList, you can use the IndexOf(T item); method to find the 0 based index of an item.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1.indexof?view=netframework-4.7.2#System_Collections_Generic_IList_1_IndexOf__0_
However, this method does not guarantee constant O(1) performance as it must search through the list. A better alternative would be to record the index as you iterate through them as you have stated.
There is no way to find the index of an arbitrary item in a list in constant time without recording the index, so that is what I would recommend.

C# - Items inside a list changing when temp value used to add items changes

I've been trying to add multiple items to a list using a temporary variable and a loop of some kind. I'll create the temp variable and list outside of the loop, and then inside the loop the temp variable will change value, and then will be added to the list as a new element before the loop repeats. The problem is, whenever the temp variable is changed, all elements in the list will change to be the same.
As an example, if I was making a list of fruits, and entered te list 'banana', 'apple' and 'pear', the list when written to the console would read 'pear', 'pear' and 'pear', which is also how the list appears in debugging mode.
The specific code I've been typing is below:
string inputText = "";
List<TextNumbers> inputTextChars = new List<TextNumbers>();
TextNumbers temp = new TextNumbers();
bool charFound = false;
inputText = Console.ReadLine();
foreach (char letter in inputText)
{
for (int i = 0; i < inputTextChars.Count; i++)
{
if (letter == inputTextChars.ElementAt(i).character)
{
charFound = true;
}
}
if (charFound == false)
{
temp.character = letter;
temp.number = 0;
}
temp.number++;
inputTextChars.Add(temp);
charFound = false;
}
inputTextChars.Sort();
foreach (TextNumbers text in inputTextChars)
{
Console.WriteLine(text.character + ": " + text.number);
}
The class 'TextNumbers' is:
public class TextNumbers
{
public char character;
public int number;
}
So as you can see from the accepted answer on this question :
Yes, a list of reference types is actually just a list of references
This means you are adding a reference to temp to the list every time.
To avoid this, move the declaration of the temp variable to within your loop so that you are adding a reference to a completely new object every time, like so:
foreach (char letter in inputText)
{
TextNumbers temp = new TextNumbers();
//..... rest of code

Generate unique list variable

I have a C# program where I have a list (List<string>) of unique strings. These strings represent the name of different cases. It is not important what is is. But they have to be unique.
cases = new List<string> { "case1", "case3", "case4" }
Sometimes I read some cases saved in a text format into my program. Sometime the a case stored on file have the same name as a case in my program.I have to rename this new case. Lets say that the name of the case I load from a file is case1.
But the trouble is. How to rename this without adding a large random string. In my case it should ideally be called case2, I do not find any good algorithm which can do that. I want to find the smalles number I can add which make it unique.
i would use a HashSet that only accepts unique values.
List<string> cases = new List<string>() { "case1", "case3", "case4" };
HashSet<string> hcases = new HashSet<string>(cases);
string Result = Enumerable.Range(1, 100).Select(x => "case" + x).First(x => hcases.Add(x));
// Result is "case2"
in this sample i try to add elements between 1 and 100 to the hashset and determine the first sucessfully Add()
If you have a list of unique strings consider to use a HashSet<string> instead. Since you want incrementing numbers that sounds as if you actually should use a custom class instead of a string. One that contains a name and a number property. Then you can increment the number and if you want the full name (or override ToString) use Name + Number.
Lets say that class is Case you could fill a HashSet<Case>. HashSet.Add returns false on duplicates. Then use a loop which increments the number until it could be added.
Something like this:
var cases = new HashSet<Case>();
// fill it ...
// later you want to add one from file:
while(!cases.Add(caseFromFile))
{
// you will get here if the set already contained one with this name+number
caseFromFile.Number++;
}
A possible implementation:
public class Case
{
public string Name { get; set; }
public int Number { get; set; }
// other properties
public override string ToString()
{
return Name + Number;
}
public override bool Equals(object obj)
{
Case other = obj as Case;
if (other == null) return false;
return other.ToString() == this.ToString();
}
public override int GetHashCode()
{
return (ToString() ?? "").GetHashCode();
}
// other methods
}
The solution is quite simple. Get the max number of case currently stored in the list, increment by one and add the new value:
var max = myList.Max(x => Convert.ToInt32(x.Substring("case".Length))) + 1;
myList.Add("case" + max);
Working fiddle.
EDIT: For filling any "holes" within your collection you may use this:
var tmp = myList;
var firstIndex = Convert.ToInt32(myList[0].Substring("case".Length));
for(int i = firstIndex; i < tmp.Count; i++) {
var curIndex = Convert.ToInt32(myList[i].Substring("case".Length));
if (curIndex != i)
{
myList.Add("case" + (curIndex + 1));
break;
}
}
It checks for every element in your list if its number behind the case is equal to its index in the list. The loop is stopped at the very first element where the condition is broken and therefor you have a hole in the list.

does passing a collection to a function means the function can change the collection's elements?

I actually know the answer to the question (I think) but I don't know the reason...
So, I know that if I have a class like the following:
class Man
{
public string Name;
public int Height;
public Man() { }
public Man(string i_name, int i_height)
{
Name = i_name;
Height = i_height;
}
}
And I have the following Program class (with main function):
class Program
{
static void Main(string[] args)
{
Program p = new Program();
Man g = new Man("greg", 175);
//assigning null to g inside the function.
p.ChangeMan(g);
Console.WriteLine(g == null? "the function changed g out side the function" : "the function did not change g out side the function");
//the output of course is that the function did not change g outside the function.
//now I am creating a list of Man and adding 5 Man instances to it.
List<Man> manList = new List<Man>();
for (int i = 0; i < 5; i++)
{
manList.Add(new Man("Gadi" + i.ToString(), 10 * i));
}
//assigning null to the list insdie the function
p.ChangeList(manList);
Console.WriteLine(manList == null ? "the function changed the list out side the function" : "the function did not change the list out side the function");
//the output of cousre again is the function did not change the list out side the function
//now comes the part I dont understand...
p.ChangeManInAList(manList);
Console.WriteLine("list count = " + manList.Count());
//count is now 6.
Console.WriteLine(manList[0] == null ? "the function changed the element out side the function" : "the function did not change the element out side the function");
//the out again - the function did not change...
}
public void ChangeMan(Man g)
{
g = null;
}
public void ChangeManInAList(IList<Man> gadiList)
{
Man g = gadiList.First<Man>();
g = null;
Console.WriteLine(g == null? "g is null" : "g is not null");
gadiList.Add(new Man("a new gadi", 200));
Console.WriteLine("list count = " + gadiList.Count());
}
public void ChangeList(List<Man> list)
{
list = null;
}
}
I am assigning null to the first element of the list + adding one Man to the list. I expected that if I can add to the list, I can also change the elements, but I saw different...
I was able to add a Man to the list but could not assign null to one of the elements, how come? I know the list is passed by value so I can not change the list itself (like assigning null to it), but I can add to it? and can not assign null to the elements? are they being passed by val as well?
will be happy for some good and clear explanation :)
Here is your point of confusion:
Man g = gadiList.First<Man>();
g = null;
What you are essentially doing is getting a Man out of the list and assigning it to the local variable g.
Then, you assign a different value to the variable g.
At no point here did you change the value of any member of the list, you simply changed the value which the variable g refers to.
Let's try to compare it to this example:
int a = 5;
int b = a;
b = 3;
//you wouldn't expect `a` to be 3 now, would you?
In order to change the value of the list item, you would need to explicitly set the list index to a different value:
Man g = gadiList.First<Man>();
gadiList[gadiList.IndexOf(g)] = null;
//or specifically in this case:
gadiList[0] = null;
When you get element from list you get new reference to list item.
So as a result you get two references: one (private reference in list object), your reference.
When you set your reference to null it do not affect reference in list object. You reference became null, but private list reference remains the same.

Categories