Using a Person class in an array - c#

Hi i have following problem:
I'm creating a program with a array of Person objects.
aantalpersonen = int.Parse(tbArray.Text); // aantal te creëren items in array;
if (aantalpersonen > 5 || aantalpersonen <= 1)
throw new ArgumentOutOfRangeException();
else
{
Persoon[] personenLijst = new Persoon[aantalpersonen];
foreach (Persoon persoon in personenLijst)
{
Persoon pers1 = new Persoon();
}
}
watching this in debugging gives me = a new array with the userdefined number of Persons,
an integer with the amount of persons.. In some other lines of code i get an integer wich holds the current chosen (to edit) Person.
All this works fine , but when i try to add data to my properties of each Person i get problems.
private void btnUpdateData_Click(object sender, RoutedEventArgs e)
{
personenLijst[huidigpersoon - 1].Naam = tbNaam.Text;
personenLijst[huidigpersoon - 1].Gewicht = int.Parse(tbGewicht.Text);
personenLijst[huidigpersoon - 1].Lengte = int.Parse(tbLengte.Text);
personenLijst[huidigpersoon - 1].Geboortedatum = dpGeboorte.SelectedDate.GetValueOrDefault(DateTime.Today);
}
this gives me following error :
System.NullReferenceException was unhandled
HResult=-2147467261
Message=Object reference not set to an instance of an object.
My first tought is i have to specificly declare each person by using the ammount of persons the user has chosen. But i can't figger out how i should do this and even if i would manage to , how can i make sure each instance of the personclass has a different name for example persoon1 ,persoon2 ..

You have declared the array to contain a specified number of Persoon objects, but you haven't set any of the elements of the array to be an actual Persoon instance.
So you cannot use something that is not there
aantalpersonen = int.Parse(tbArray.Text);
if (aantalpersonen > 5 || aantalpersonen <= 1)
throw new ArgumentOutOfRangeException();
else
{
Persoon[] personenLijst = new Persoon[aantalpersonen];
for( int x = 0; x < personenLijst.Length ; x++)
{
personenLijst[x] = new Persoon();
}
}
After that your array is filled with instances of Persoon class and you can change the properties of the individual instances. However, as noted by Mr Holterman in its comment below, the array is declared as a local variable in this snippet of code, so it is not available (not in scope) in the event handler where you try to change the individual properties. To fix this problem the array should be declared at the global level.
Persoon[] personenLijst = null;
and initialized with
personenLijst = new Persoon[aantalpersonen];
Said that, why do you still use arrays instead of a more versatile List<Persoon>

Your problem comes from the fact you didn't initialize your array members.
You create a bunch of "Person" but they are not stored into your array. As .NET states, an array is by default initialized with all columns set to null.
Your code should be :
Persoon[] personenLijst = new Persoon[aantalpersonen];
int i = 0;
foreach (Persoon persoon in personenLijst)
{
personenLijst[i] = new Persoon();
i++;
}

Ok this works, thanks the problem was that i did this : Persoon[] personenLijst = new Persoon[aantalpersonen]; after the else loop, which created a NEW array next to the first i declared at global level
by putting this instead personenLijst = new Persoon[aantalpersonen]; everything works fine!
Thanks for your help guys !
Appreciate it

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).

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.

Create instance of object every nth time through a loop

Apologies if I'm struggling to word this properly. OOP is not my expertise but I'm very much trying to learn.
How do I create an instance of an object on say, every third iteration of a loop?
Within a loop, I need to assign values to an object, but the property to assign a value to will depend on the result of a case statement. Once each property of the object has been assigned, I then need to add that object to a list of objects of the same type.
If I create the object before the loop is entered, then my list just contains the same result over and over again, because (I've read) that the list only contains a reference to the object, and if the object is then changed, so does the list.
If I create the object within the loop, then obviously, I'll get a new object each time with just one of the properties assigned to it by the time it adds it to the list. The list would contain different results, but only the last property would be assigned, as a new object is created each time.
What I assumed you could therefore do was create a new object whenever all of the properties had a value assigned to it (or at the start, when none had). So, since my object has three properties, each time through the loop, I would like to add a new object whenever an int iCounter was 0, add the values, and increment iCounter, then when iCounter is 3, set to 0. However, when I attempt to create an object inside of an if statement, the rest of the program doesn't see the object exists.
I also assumed, I could maybe attempt some kind of macro substitution, which is what I would normally resort to in Fox, however, (I've read) that this is a big no-no in c#.
Any ideas?
try
{
cProducts Product = new cProducts();
SqlConn2.Open();
rdr2 = SqlComm2.ExecuteReader();
int iScanLine = 0;
while (rdr2.Read())
{
iScanLine++;
Product.product = rdr2["product"].ToString();
Product.sOrder = rdr2["order_id"].ToString();
switch (rdr2["detail"].ToString())
{
case "Quantity":
Product.quantity = Convert.ToInt16(rdr2["display_value"]) ;
break;
case "Option":
Product.Option = rdr2["display_value"].ToString();
break;
case "Size":
Product.Size = rdr2["display_value"].ToString();
break;
}
if (iScanLine == 3)
{
lProducts.Add(Product);
thisPage.sProducts.Add(lProducts[lProducts.Count() - 1]);
iScanLine = 0;
}
}
}
You could just change this bit:
if (iScanLine == 3)
{
lProducts.Add(Product);
thisPage.sProducts.Add(Product); //<-- We know the object just added is still in Product
iScanLine = 0;
Product = new cProducts(); //<-- Create a new object to start populating
}
Also, I know that .NET framework is quite new, being only a decade old, but you might consider reading the Naming Guidelines:
X DO NOT use Hungarian notation.
Looks like you have table with four columns, where each product represented in three consecutive rows
product | order_id | detail | display_value
A X Quantity 5
A X Option Foo
A X Size XL
B X Quantity 2
...
And you are trying to read products. I suggest you to store current product name and compare it with last product name. If name is changed, then you are reading data of next product, thus you can create new product and add it to list of products:
IDataReader reader = SqlComm2.ExecuteReader();
List<Product> products = new List<Product>();
Product product = null;
while (reader.Read())
{
var name = reader["product"].ToString();
if (product == null || product.Name != name) // check if new product
{
product = new Product(); // create new product
product.Name = name; // fill name
product.OrderId = reader["order_id"].ToString(); // and order
products.Add(product); // add to products
}
object value = reader["display_value"]; // get value from row
switch (reader["detail"].ToString())
{
case "Quantity":
product.Quantity = Convert.ToInt16(value);
break;
case "Option":
product.Option = value.ToString();
break;
case "Size":
product.Size = value.ToString();
break;
}
}
As you can see, I also refactored naming - PascalCase for properties, camelCase for local variables, no Hungarian notation. Also new names for properties introduced - Product.Name instead of odd Product.Product, OrderId instead of sOrder.
Use the Modulus operator to check whether the iterating variable is divisible by expected nth value or not
if(value % 3 == 0)
{
//do stuff
}
value++;
You are repeatedly adding the same Product to your list, and never create a new one. When you get to the end of your loop, it'll appear as though you've only got a single item in there.
After you've added your item (within the if (iScanLine == 3)), I suspect you want to create a new item: Product = new cProducts().
Also, I would like to reference this particular comment that you make in your question:
If I create the object before the loop is entered, then my list just
contains the same result over and over again, because (I've read) that
the list only contains a reference to the object, and if the object is
then changed, so does the list.
The following code will result in 5 separate objects being added to a list:
List<cProducts> list = new List<cProducts>();
cProducts Product = new cProducts();
for (int i = 0; i < 5; i++)
{
list.Add(Product);
Product = new cProducts();
}
You are correct that the list only contains references to the objects - but you are not changing any of the objects; you are creating new ones. This is a fundamental programming principle, and I'd suggest you take the time to understand how it works before moving on.
Not sure if I understand completely but the following loop whould use a counter to execute every third time
int isThirdTime = 0; //Test condition for third time equality
while (true) //neverending loop
{
if (isThirdTime == 3)//test for 3rd time
{
// add to list
isThirdTime = 0; //reset counter
}
isThirdTime++; // Increase the counter
}

Checking for duplicates in an object array?

I have the following object array, created from a custom class Room. Before I add a new Room to the array I want to verify that the Roomname is not already in the array. Here is a sample of what I am trying:
private void btnAddRm_Click(object sender, EventArgs e)
{
Room[] roomArray = new Room[20];
test = txtName.text;
for (int i = 0; i < roomArray.length; i++)
{
if(test != roomArray[i].getRoomName())
{
addRoom();
}
}
}
GetRoomName() is the accessor that retrieves the RoomName from the class. I guess my question is why is this code not working?
Thank you
Your current code will add the room as soon as the room's name doesn't equal the new room.
Instead, first check that none of the rooms have the existing name and only then add it:
private void btnAddRm_Click(object sender, EventArgs e)
{
Room[] roomArray = new Room[20];
test = txtName.text;
bool exists = false;
for (int i = 0; i < roomArray.length; i++)
{
if (test == roomArray[i].getRoomName())
{
exists = true;
break;
}
}
if (!exists)
{
addRoom();
}
}
Your code fires addRoom() every time there is non-matching room in current array.
I think, you're looking for something like that:
if(!roomArray.Any(r => r.getRoomName() == test))
addRoom();
or using All method:
if(roomArray.All(r => r.getRoomName() != test))
addRoom();
You should finish looping through the entire array prior to adding the new room. With what you have if you have a new room name that doesn't match any existing rooms it'll call addRoom() 20 times.
Room[] roomArray = new Room[20];
test = txtName.text;
bool doesRoomExist = false;
for (int i = 0; i < roomArray.length; i++)
{
if (test == roomArray[i].getRoomName())
{
doesRoomExist = true;
break;
}
}
if (!doesRoomExist)
addRoom();
You can also condense you're for loop by using the Any extension method, you will need using System.Linq to do so.
if (!roomArray.Any(room => room.GetRoomName() == test))
addRoom();
Like others already stated your code does not make any sense. You create an array of rooms leaving all elements uninitialized (null). Then you check for existence of a room within that array. Also in your loop you "add" the new room each time an element has a different name. I have no clue how you want to add something to an array that is outside of the scope of your addRoom method. I hope this is not your actual code!
So first of all the room array should be created as a field of your class outside the scope of the btnAddRm_Click method.
Since you want to add rooms you should not use an array. Use a List<Room> instead. Otherwise you would need to know how many elements of your array are already initialized with a room. Also you would need to grow your array if the number exceeds the initial size of the array. This is exactly what List<Room> can do for you.
Your btnAddRm_Click event handler can then use LINQ to check for the existence of any room with the same name.
Please notice that you should use String.Compare(name1, name2) instead of name1 == name2.
private List<Room> _theRooms = new List<Room>();
private void btnAddRm_Click(object sender, EventArgs e)
{
if (!_theRooms.Any(r => string.Compare(r.Name, txtName.Text, StringComparison.CurrentCultureIgnoreCase) == 0))
{
addRoom();
}
}
Assuming that the room name is some kind of unique key for rooms you can also use a HashSet<Room> and then add the room anyway. HashSet will check for existence of the room. Your Room class however needs to overwrite Equals and GetHashCode then with Equals checking for equality of the room names.

Array of Lists throws NullReferenceException

I am trying to make a 1d array of lists. I make it like this:
public static List<string>[] words = new List<string>[30];
public static List<string>[] hints = new List<string>[30];
And I call it like this:
foreach (string item in vars.directory)
{
reader2 = new StreamReader(item);
while (reader2.Peek() > 0)
{
string line = reader2.ReadLine();
if (line.StartsWith("#"))
{
vars.words[counter].Add(line.Substring(1, line.Length - 1)); //here
}
else if (line.StartsWith("-"))
{
vars.hints[counter].Add(line.Substring(1, line.Length - 1)); //another here
}
else if (line == "#end")
{
counter++;
}
}
}
I just wanted to add that vars is where I keep my public variables and that counter is indeed at 0 when the loop starts.
EDIT
In my haste I forgot to add the question... oops...
Here it is: When I call the add function (or any another function for that matter) it returns a null reference exception. How can I fix this?
I assume you're crashing when attempting to call .Add on your array element. You need to initialize your arrays with valid objects.
for( Int32 i = 0; i < vars.words.Length; ++i )
vars.words[i] = new List<string>();
for( Int32 i = 0; i < vars.hints.Length; ++i )
vars.hints[i] = new List<string>();
Why not just make a List<List<string>>, but yes you can make an array of lists
Using a list of lists, as already recommended, would make you escape your problems,
and it´s much more flexible and handy than your construction.
-> f.i. if the size of your data changes, you don´t have to change the list size, but the array
Here's a one-liner to initialize an array of lists of size 30:
static List<string>[] lists = (from i in Enumerable.Range(0, 30)
select new List<string>()).ToArray();
The problem is that array values are initialized to the default value, and the default value for reference types is null.
default(List<string>) returns null.
So, you'll need to re-initialize the objects in the array before you can access them, otherwise you will get a NullReferenceException.
One way to initialize all the objects in your array up front is to use this Linq statement:
const int sizeOfLists = 5;
List<string>[] lists = Enumerable.Range(0, sizeOfLists)
.Select(i => new List<string>())
.ToArray();
Another option is to initialize and add the sub-lists only when you need them, by using an outer List:
var lists = new List<List<string>>();
// ...
var aSubList = new List<string>();
lists.Add(aSubList);
This is particularly useful if you don't know the size of the outer set of lists up-front, and is still accessible by index.
(This was a comment before, but I made it an answer since many other answers got caught up in the solution and don't describe the problem)
You could initialize the lists right before you use them:
foreach (string item in vars.directory)
{
reader2 = new StreamReader(item);
while (reader2.Peek() > 0)
{
string line = reader2.ReadLine();
// new code
if (vars.words[counter] == null) vars.words[counter] = new List<string>();
if (vars.hints[counter] == null) vars.hints[counter] = new List<string>();
if (line.StartsWith("#"))
{
vars.words[counter].Add(line.Substring(1, line.Length - 1)); //here
}
else if (line.StartsWith("-"))
{
vars.hints[counter].Add(line.Substring(1, line.Length - 1)); //another here
}
else if (line == "#end")
{
counter++;
}
}
}

Categories