For example i have the following class:
class Person
{
public List<int> Grades{ get; set; }
public Person(List<int> grades)
{
this.Grades = grades;
}
}
And than i use this class in a main method , something like:
static void Main(string[] args)
{
List<int> grades = new List<int> { 1, 2, 3, 4, 5 };
Person person = new Person(grades);
for (int i = 0; i < 5; i++)
{
if (i % 2 == 0)
{
grades[i] = 0;
}
else
{
grades[i] = -1;
}
}
foreach(var grade in person.Grades)
{
Console.Write(grade + " ");
}
Console.ReadKey();
}
After running this code i expected to have on console the:
1 2 3 4 5
output result.
Insteead i have this output result:
0 -1 0 -1 0
I expected that the collection "saved" into Person instance to stay how it was initialized. What should i do to keep this collection unmodified for Person instances, so that i will have the "1 2 3 4 5" output result even if i modify the grades collection in main method.
Can someone please explain me, why is this happening ?
I expected that the collection "saved" into Person instance to stay how it was initialized.
It's time to revisit your expectations of how reference types work in C# then :) I have an article that you might find useful...
What should i do to keep this collection unmodified for Person instances, so that i will have the "1 2 3 4 5" output result even if i modify the grades collection in main method.
You can copy the collection in the constructor:
public Person(List<int> grades)
{
this.Grades = new List<int>(grades);
}
Now for a List<int> that's fine - if you have a list of a mutable type, however, you potentially want to clone each element too. This sort of thing is why immutable types are nice - it's much easier to reason about their behaviour, because nothing can mess with instances, so you can just keep references...
Both the local variable grades and the field in the Person class are both referencing the same list. Evaluating either variable to its values result in the same list being returned, therefore mutating that one list that exists creates changes that are observable from either variable.
If you create a new list and copy all of the values from it to the new list when assigning the property's value in the constructor then you will have two separate lists, and mutating one won't be observable through the other.
this.Grades = grades;
The above line only assign the reference held by grades to this.Grades so now both are pointing/referencing the same item in memory.
To resolve it, since it is a List<int>, you can do
this.Grades = grades.ToList();
But if you have List<T> where T is a class or a reference type, you will still have the same problem. See: How create a new deep copy (clone) of a List<T>?
Lists are reference objects.
When you pass the list to the new person object, what you're really doing is passing it a pointer to the list, so the list in the person and the local list variable grades are actually the same list.
You can have the person object create a copy of the list instead using either of these approaches:
this.Grades = grades.ToList();
// or
this.Grades = new List<int>(grades);
Related
I have an array of List types:
List<object>[] vector = new List<object>[3];
The first List contains strings:
// Get word lists together, remove duplicates
var words = tableA.ToList().Union(tableB.ToList());
// Sort words
words = words.OrderBy(s => s, StringComparer.CurrentCultureIgnoreCase);
// Add words to the vector first slot
vector[0] = words.ToList<object>();
Now, I want to add ints to the second and third lists, but I get an error here:
vector[1].Add(tableA.GetValue(keyword));
vector[2].Add(tableB.GetValue(keyword));
GetValue() returns an int. But when I add these ints to the vector Lists it throws error:
ERROR Caught: Object reference not set to an instance of an object.
How should I add the ints to the List? Or is there some other data structure I should use instead for the vector? I feel there is some trivial cast I'm missing but I haven't been able find a solution.
I'm not an expert in C#, but i think i understand.
When you write :
List<object> vector = new List<object>[3];
you create a table of List with a size of 3.
You can put something into each slot of this array, but each "slot" still refers to no instance after this first line of code.
When you write
vector[0] = words.ToList<object>();
You put somehting into the first slot of vector list. But [1] and [2] are still empty. And
vector[1]
refers to a reference not set to an instance of an object. In short terms, it refers to nothing.
You must initialize each vector index value before add value. Thanks
When writing var a = new List<object> you´re only declaring that a is a list holding some (in your case three) items. However you don´t determine what stands in those three elements. You´d have to out some values into every single item, before you can anything with it (e.g. call any method).
You´re allready putting a list into the first item, however the elements on index one and two remain null causing a NullReferenceException when calling a method like the following:
vector[1].Add(...);
So you should initialize the value at index oe and two before:
vector[1] = new List<int>();
vector[2] = new List<int>();
But still you can´t do much with the list, because it is of type object, so you´d have to cast every element to the actual type:
((List<int>)vector[1]).Add(myInt);
Anyway I doubt storing three completely different lists within one single list alltogether is a good idea. Maybe you should define a class with the three lists as members instead:
class MyClass
{
public List<string> Words { get; set; }
public List<int> NumbersA { get; set; }
public List<int> NumbersB { get; set; }
}
public class Teacher{
public string imageUrl;
public TeacherEducationalQualification[] teacherEducationalQualification;
}
public class TeacherEducationalQualification{
public string NameOfDegree;
public string NameOfUniversity;
public int YearOfGraduation;
}
In the above codes when I instantiate Teacher class like
Teacher teacher= new Teacher();
This works fine but when I instantiate array in Teacher class for object 'teacher'
teacher.teacherEducationalQualification = new TeacherEducationalQualification[5];
It gives me an error 'Object reference not set to an instance of an object' whenever i try to access any variable to set values in it.
teacher.teacherEducationalQualification[1].NameOfDegree= "abc";
Please Help.
After you initialize an array of objects (reference type) its items will be null. You have to iterate (loop) through the array and initialize each item.
If you want to set each item individually however, you can do something like this
Teacher teacher = new Teacher();
teacher.teacherEducationalQualification = new TeacherEducationalQualification[5];
// Initialize item at index 0; indices start with 0 so the 1st item has index 0
teacher.teacherEducationalQualification[0] = new TeacherEducationalQualification();
teacher.teacherEducationalQualification[0].NameOfDegree= "abc";
// Initialize item at index 1
// Initialize item at index 2
// Initialize item at index 3
// Initialize item at index 4; this is the last index, your 5th item
teacher.teacherEducationalQualification[4] = new TeacherEducationalQualification();
teacher.teacherEducationalQualification[4].NameOfDegree= "xyz";
// Or in a different way with the help of a local variable
var qualification;
qualification = new TeacherEducationalQualification();
qualification.NameOfDegree= "abc";
// set other fields
teacher.teacherEducationalQualification[0] = qualification;
// ...
qualification = new TeacherEducationalQualification();
qualification.NameOfDegree= "xyz";
// set other fields
teacher.teacherEducationalQualification[4] = qualification; // last item
Note: field names in C# should be camelCase - start with a lowercase letter
public string nameOfDegree;
Properties, on the other hand, should be PascalCase - start with an uppercase letter
public string NameOfDegree { get; set; } // auto-implemented property
When you instantiate an array you are essentially creating a data structure which is capable of holding a number of objects next to each other, however the objects (i.e., the array elements) must be instantiated separately. When an array is created, all elements of the array are initialised with the default value of the array type. For example for an array of integers all elements would be 0, for an array of DateTime all elements would be DateTime.MinValue and for an array of any reference type (like your example above) the elements will be null. That's why you got a NullReferenceException. If you like to instantiate an array as well as all elements using the default constructor you can use the following:
teacher.teacherEducationalQualification = new TeacherEducationalQualification[5];
for(int i = 0; i < teacher.teacherEducationalQualification.Length; i++)
teacher.teacherEducationalQualification[i] = new TeacherEducationalQualification();
After that, it'll be safe to assign to properties of each array element. My answer above does not necessarily mean this is the best design to solve this problem though.
When you instantiate a array of objects in c# you instantiate an array with null values:
teacher.teacherEducationalQualification = new TeacherEducationalQualification[5];
equals to
teacher.teacherEducationalQualification = new TeacherEducationalQualification[]{null, null, null, null, null};
so
teacher.teacherEducationalQualification[1] == null
you must instantiate the object before use it:
teacher.teacherEducationalQualification[1] = new TeacherEducationalQualification();
teacher.teacherEducationalQualification[1].NameOfDegree= "abc"
Otherwise if you don't want to create instances of Object, you need to use struct:
public struct TeacherEducationalQualification{
public string NameOfDegree;
public string NameOfUniversity;
public int YearOfGraduation;
}
Could someone please explain the behavior of this
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
struct person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 1 - 23
Person - 2 - 24
*/
Why am I not getting the same output for a class as that of a struct?
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
class person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 2 - 24
Person - 2 - 24
*/
Classes are reference types, structs are value types.
When a value type is passed to a method as a parameter, a copy of it will be passed through. That means that you add two completely separate copies of the Person struct, one for each pass in the loop.
When a reference type is passed to a method as a parameter, the reference will be passed through. That mean that you add two copies of the reference to the same memory location (to the same Person object) - when making changes to this one object, you see it reflected in both references since they both reference the same object.
It's the difference between value type (struct) and reference type (class).
When you're adding the struct to Listperson the content of person is put in the list, you have two different person struct in your list.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains a person struct with value { age = 23, name = 1}
Second iteration:
Listperson contains a person struct with value { age = 23, name = 1}
Listperson contains another person struct with value { age = 24, name = 2}
*/
}
When you're adding the class the reference is put in the list, you have two references that referenced the same person object.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains 1 reference to myperson object with value { age = 23, name = 1}
Second iteration:
Listperson contains 2 reference to myperson object with value { age = 24, name = 2}
*/
}
Because your myperson variable only ever deals with one person struct/class.
What you add to the list, in your loop, is a copy of your myperson variable - which for the struct, will be an entire copy of the struct, but for the class will be a copy of the reference to the single instance that you create (and mutate).
If you want same result then bring person declaration inside of the for loop:-
// person myperson = new person();
//Move the upper line inside the for loop
for (int i = 1; i <= 2; i++)
{
person myperson = new person();
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
In struct you adding a value type hence separate values are stored, whereas in class you are adding reference to the object hence gettng same value.
In the second instance, you're addding a reference type. In fact, you're adding the same item, twice, since your
= new person()
is not in the loop. So it always points to the same object you initialized here:
person myperson = new person();
Even after it's added to your list, the changes affect it.
In the first instance, you're adding a struct each time, which is a value type, so will be copied into the list. Changes you make after that no longer refer to the object in the list, so they have different values.
Structures are value types and classes are reference types. So in your first example when you add myperson to the list your adding a copy of myperson and the myperson variable still refers to a separate copy. In you second example myperson is a reference type so your adding two pointers to the same object.
You should understand key distinctions between structs (Value Types) and classes (Reference Type). You could easily find this information in Google or at SO.
When you add struct instance to List you create another separate copy for this instance, and when you change one element you did not change another.
But in case of classes you create one instance and uses this one "shared" instance with two references (list[0] and list1) and you could change this one instance through two different references, that's why when you change list[0] item it seems that you change list1 item too.
Consider following code:
var s1 = new SampleStruct { X = 1, Y = 1 };
var s2 = s1;
//Creating separate copy
//Lets check this
Console.WriteLine(object.ReferenceEquals(s1, s2)); //Prints False
var c1 = new SampleClass { X = 1, Y = 2 };
var c2 = c1;
//We do not create any copy
// two references c1 and c2 "pointed" to one shared object
Console.WriteLine(object.ReferenceEquals(c1, c2)); //Prints True
Similar behavior we have when we pass parameter to function (or adding element to list).
In the second example you're only creating on item and adding a reference to to the list many times.
When you add the struct to the collection, it makes a copy of it. It's a value type. You'll end up with two distinct objects in the collection, each with different values. This is probably the expected behavior.
When you add the class, the reference type, to the collection, a new object is not created. You're actually adding two different references to the same object. You'll end up with (apparently) two objects with the same value. It's actually the same object, seemingly appearing twice in the collection.
Think of variables and parameters of class types as holding an "instance IDs". The only things one can actually do directly with an instance ID are (1) create a new one (which will be assigned to a new instance of a class), (2) assign one to another, or (3) check two IDs to see if they are equal. Doing anything else with a variable, parameter, etc. of a class type is a short hand for "do _ to the instance referred to this instance ID".
So code like:
{
Car A,B,C; /* Car is a class */
A = new Car;
B = new Car;
C = A;
A.color = carColors.Yellow;
B.color = C.color;
}
The first "new" statement will create an instance of Car and put its instance ID (let's say #1234) in "A". The second will create another car instance (#4321) and store its ID in B. The next statement will copy #1234 into C. It doesn't do anything with the car--it just copies the ID. Then car #1234 will be painted yellow, then in the last statement, the color of car #1234 (i.e. yellow) will be used to paint car #4321. Note that while A and C are different variables, they both hold the same instance ID (#1234) and thus refer to the same car.
Could someone please explain the behavior of this
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
struct person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 1 - 23
Person - 2 - 24
*/
Why am I not getting the same output for a class as that of a struct?
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
class person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 2 - 24
Person - 2 - 24
*/
Classes are reference types, structs are value types.
When a value type is passed to a method as a parameter, a copy of it will be passed through. That means that you add two completely separate copies of the Person struct, one for each pass in the loop.
When a reference type is passed to a method as a parameter, the reference will be passed through. That mean that you add two copies of the reference to the same memory location (to the same Person object) - when making changes to this one object, you see it reflected in both references since they both reference the same object.
It's the difference between value type (struct) and reference type (class).
When you're adding the struct to Listperson the content of person is put in the list, you have two different person struct in your list.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains a person struct with value { age = 23, name = 1}
Second iteration:
Listperson contains a person struct with value { age = 23, name = 1}
Listperson contains another person struct with value { age = 24, name = 2}
*/
}
When you're adding the class the reference is put in the list, you have two references that referenced the same person object.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains 1 reference to myperson object with value { age = 23, name = 1}
Second iteration:
Listperson contains 2 reference to myperson object with value { age = 24, name = 2}
*/
}
Because your myperson variable only ever deals with one person struct/class.
What you add to the list, in your loop, is a copy of your myperson variable - which for the struct, will be an entire copy of the struct, but for the class will be a copy of the reference to the single instance that you create (and mutate).
If you want same result then bring person declaration inside of the for loop:-
// person myperson = new person();
//Move the upper line inside the for loop
for (int i = 1; i <= 2; i++)
{
person myperson = new person();
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
In struct you adding a value type hence separate values are stored, whereas in class you are adding reference to the object hence gettng same value.
In the second instance, you're addding a reference type. In fact, you're adding the same item, twice, since your
= new person()
is not in the loop. So it always points to the same object you initialized here:
person myperson = new person();
Even after it's added to your list, the changes affect it.
In the first instance, you're adding a struct each time, which is a value type, so will be copied into the list. Changes you make after that no longer refer to the object in the list, so they have different values.
Structures are value types and classes are reference types. So in your first example when you add myperson to the list your adding a copy of myperson and the myperson variable still refers to a separate copy. In you second example myperson is a reference type so your adding two pointers to the same object.
You should understand key distinctions between structs (Value Types) and classes (Reference Type). You could easily find this information in Google or at SO.
When you add struct instance to List you create another separate copy for this instance, and when you change one element you did not change another.
But in case of classes you create one instance and uses this one "shared" instance with two references (list[0] and list1) and you could change this one instance through two different references, that's why when you change list[0] item it seems that you change list1 item too.
Consider following code:
var s1 = new SampleStruct { X = 1, Y = 1 };
var s2 = s1;
//Creating separate copy
//Lets check this
Console.WriteLine(object.ReferenceEquals(s1, s2)); //Prints False
var c1 = new SampleClass { X = 1, Y = 2 };
var c2 = c1;
//We do not create any copy
// two references c1 and c2 "pointed" to one shared object
Console.WriteLine(object.ReferenceEquals(c1, c2)); //Prints True
Similar behavior we have when we pass parameter to function (or adding element to list).
In the second example you're only creating on item and adding a reference to to the list many times.
When you add the struct to the collection, it makes a copy of it. It's a value type. You'll end up with two distinct objects in the collection, each with different values. This is probably the expected behavior.
When you add the class, the reference type, to the collection, a new object is not created. You're actually adding two different references to the same object. You'll end up with (apparently) two objects with the same value. It's actually the same object, seemingly appearing twice in the collection.
Think of variables and parameters of class types as holding an "instance IDs". The only things one can actually do directly with an instance ID are (1) create a new one (which will be assigned to a new instance of a class), (2) assign one to another, or (3) check two IDs to see if they are equal. Doing anything else with a variable, parameter, etc. of a class type is a short hand for "do _ to the instance referred to this instance ID".
So code like:
{
Car A,B,C; /* Car is a class */
A = new Car;
B = new Car;
C = A;
A.color = carColors.Yellow;
B.color = C.color;
}
The first "new" statement will create an instance of Car and put its instance ID (let's say #1234) in "A". The second will create another car instance (#4321) and store its ID in B. The next statement will copy #1234 into C. It doesn't do anything with the car--it just copies the ID. Then car #1234 will be painted yellow, then in the last statement, the color of car #1234 (i.e. yellow) will be used to paint car #4321. Note that while A and C are different variables, they both hold the same instance ID (#1234) and thus refer to the same car.
my problem is as follows:
Im building a console application which asks the user for the numbers of objects it should create and 4 variables that have to be assigned for every object.
The new objects name should contain a counting number starting from 1.
How would you solve this?
Im thinking about a class but im unsure about how to create the objects in runtime from userinput. Is a loop the best way to go?
What kind of class, struct, list, array .... would you recommend. The variables in the object are always the same type but i need to name them properly so I can effectivly write methods to perform operations on them in a later phase of the program.
Im just learning the language and I would be very thankful for a advice on how to approach my problem.
If I understand your problem correctly:
class MyClass
{
public int ObjectNumber { get; set; }
public string SomeVariable { get; set; }
public string AnotherVariable { get; set; }
}
// You should use keyboard input value for this
int objectsToCreate = 10;
// Create an array to hold all your objects
MyClass[] myObjects = new MyClass[objectsToCreate];
for (int i = 0; i < objectsToCreate; i++)
{
// Instantiate a new object, set it's number and
// some other properties
myObjects[i] = new MyClass()
{
ObjectNumber = i + 1,
SomeVariable = "SomeValue",
AnotherVariable = "AnotherValue"
};
}
This doesn't quite do what you described. Add in keyboard input and stuff :) Most of this code needs to be in some kind of Main method to actually run, etc.
In this case, I've chosen a class to hold your 4 variables. I have only implemented 3 though, and I've implemented them as properties, rather than fields. I'm not sure this is necessary for your assignment, but it is generally a good habit to not have publically accessible fields, and I don't want to be the one to teach you bad habits. See auto-implemented properties.
You mentioned a struct, which would be an option as well, depending on what you want to store in it. Generally though, a class would be a safer bet.
A loop would indeed be the way to go to initialize your objects. In this case, a for loop is most practical. It starts counting at 0, because we're putting the objects in an array, and array indexes in C# always start at 0. This means you have to use i + 1 to assign to the object number, or the objects would be numbered 0 - 9, just like their indexes in the array.
I'm initializing the objects using object initializer syntax, which is new in C# 3.0.
The old fashioned way would be to assign them one by one:
myObjects[i] = new MyClass();
myObjects[i].ObjectNumber = i + 1;
myObjects[i].SomeVariable = "SomeValue";
Alternatively, you could define a constructor for MyClass that takes 3 parameters.
One last thing: some people here posted answers which use a generic List (List<MyClass>) instead of an array. This will work fine, but in my example I chose to use the most basic form you could use. A List does not have a fixed size, unlike an array (notice how I initialized the array). Lists are great if you want to add more items later, or if you have no idea beforehand how many items you will need to store. However, in this case, we have the keyboard input, so we know exactly how many items we'll have. Thus: array. It will implicitly tell whoever is reading your code, that you do not intend to add more items later.
I hope this answered some questions, and raised some new ones. See just how deep the rabbit hole goes :P
Use a list or an array. List example:
int numberOfObjects = 3;
List<YourType> listOfObjects = new List<YourType>();
for(int i = 0 ; i < numberOfObjects ; i++ )
{
// Get input and create object ....
// Then add to your list
listOfObjects.Add(element);
}
Here, listOfObjects is a Generic list that can contain a variable number of objects of the type YourType. The list will automatically resize so it can hold the number of objects you add to it. Hope this helps.
If I understood what you are asking you could probably do something like this:
class Foo
{
private static int count;
public string name;
public Foo(...){
name = ++count + "";
}
}
I'm guessing what you're trying to do here, but this is a stab in the dark. The problem I'm having is dealing with the whole "the new objects name should contain a counting number starting from 1" thing. Anyway, here's my attempt:
public class UserInstantiatedClass
{
public int UserSetField1;
public int UserSetField2;
public int UserSetField3;
public int UserSetField4;
public string UserSpecifiedClassName;
}
public static class MyProgram
{
public static void Main(string [] args)
{
// gather user input, place into variables named
// numInstances, className, field1, field2, field3, field4
List<UserInstantiatedClass> instances = new List< UserInstantiatedClass>();
UserInstantiatedClass current = null;
for(int i=1; i<=numInstances; i++)
{
current = new UserInstantiatedClass();
current.UserSpecifiedClassName = className + i.ToString(); // adds the number 1, 2, 3, etc. to the class name specified
current.UserSetField1 = field1;
current.UserSetField2 = field2;
current.UserSetField3 = field3;
current.UserSetField4 = field4;
instances.Add(current);
}
// after this loop, the instances list contains the number of instances of the class UserInstantiatedClass specified by the numInstances variable.
}
}