Why does aFooList contain five copies of the last item, instead of the five items I inserted?
Expected Output: 01234
Actual Output: 44444
using System;
using System.Collections.Generic;
namespace myTestConsole {
public class foo {
public int bar;
}
class Program {
static void Main(string[] args) {
foo aFoo = new foo(); // Make a foo
List<foo> aFooList = new List<foo>(); // Make a foo list
for (int i = 0; i<5; i++) {
aFoo.bar = i;
aFooList.Add(aFoo);
}
for (int i = 0; i<5; i++) {
Console.Write(aFooList[i].bar);
}
}
}
}
You have added the same item, aFoo, 5 times. When you modify contents of a reference-type object you don't create new copies, you modify the same object.
List<foo> aFooList = new List<foo>(); // Make a foo list
for (int i = 0; i<5; i++) {
foo aFoo = new foo(); // Make a foo
aFoo.bar = i;
aFooList.Add(aFoo);
}
You are still modifying your aFoo while it is in the list.
You've added a reference to the same object to your list 5 times, each time modify the value of bar. Add this line after aFooList.Add(aFoo); to see the effect each time you add a foo to the list.
Console.WriteLine(string.Join("", foos.Select(f => f.bar)));
For what it's worth, it's a one-liner (spaced nicely for readability) with Linq.
var foos = Enumerable.Range(0, 5)
.Select(n => new foo {bar = n})
.ToList();
Complete example:
foo aFoo = new foo(); // Make a foo
List<foo> aFooList = new List<foo>(); // Make a foo list
Console.WriteLine("\nUsing aFooList");
for (int i = 0; i < 5; i++)
{
aFoo.bar = i;
aFooList.Add(aFoo);
Console.WriteLine(string.Join("", aFooList.Select(f => f.bar)));
}
var foos = Enumerable.Range(0, 5).Select(n => new foo { bar = n }).ToList();
Console.WriteLine("\nUsing foos");
Console.WriteLine(string.Join("", foos.Select(f => f.bar)));
Console.ReadLine();
Output:
Using aFooList
0
11
222
3333
44444
Using foos
01234
Your variable 'aFoo' is basically a pointer to a location in memory. Without "newing" up another instance (malloc) you're modifying the same location in memory and add the same pointer 5 times.
The "new" keyword fullfills the function as far as programmers are concerned in C# as malloc does in C. It will get a new memory location and make aFoo point to that location, instead of the old one.
From MSDN:
The new operator is used to create objects and invoke constructors
MSDN
Related
I want the user to be able to specify the number of instances there are. I find that a good way to do this is using a for loop.
class Instance
{
public string name;
public int health;
public int dmg;
public Instance()
{
name = Instance;
health = 100;
dmg = 10;
}
class Program {
static void Main(string[] args) {
instance[] instanceArray = new instance
Console.WriteLine("how many instances will there be?");
string inp = Console.ReadLine();
for (int i = 0; i >= inp; i++) {
//TODO: I don't know what would go in here
}
}
}
You should create an instance in the for loop and assign it to array's item:
static void Main(string[] args) {
// In case of array, you should specify its length beforehead
// So, let's ask user about the number of instances to create
Console.WriteLine("how many instances will there be?");
// Note, int imp (not string): number of instances must be integer
// like 3, 5, 8, not string as "bla-bla-bla"
//TODO: int.TryParse is a better approach
int inp = int.Parse(Console.ReadLine());
// We create an array to hold inp instances
instance[] instanceArray = new instance[inp];
// Time to create inp instances and put them to array:
for (int i = 0; i < inp; i++) {
// Create an instance and assign it to i-th item:
instance[i] = new Instance();
}
// from now you have instance array with inp items
}
A bit shorter way to create the same array is Linq:
using System.Linq;
...
instance[] instanceArray = Enumerable
.Range(1, inp)
.Select(_ => new Instance())
.ToArray();
You can use Enumerable.Range for that:
instance[] instanceArray = Enumerable.Range(0, inp).Select(i => new instance()).ToArray();
Or just initiate the Objects via the loop like you started doing:
instance[] instanceArray = new instance[inp];
for (int i=0; i<inp ;++i){
instance[i] = new instance();
}
Cbooks has an atribute "CTeam[] Teams" and it is of fixed size (8). If I want to add objects to it using this in the Main:
CBook A1 = new CBook("Title1", "Author1");
CBook A2 = new CBook("Title1", "Author2");
CBooks ArrayOfBooks = new CBooks(8);
ArrayOfBooks.Add(A1);
ArrayOfBooks.Add(A2);
then position 0 and 1 are ocuppied, and the positions from 2 to 7 are null. What I want to do is, using a variable "int aux=0", count the ocupied positions like this:
for (int k = 0; k < NumberOfTeams; k++)
{
if (Teams[k].Name=="")
Aux += 1;
}
So, Aux in this case would be 2, then I want to do "Teams[Aux] = A" so that A would be in the position 2 and now I should have three objects in my array. But I'm getting "Index out of bound"
Your implementation then should look similar to this:
public class Program
{
public static void Main(string[] args)
{
Element a = new Element("A");
Element b = new Element("B");
MyArray array = new MyArray(8);
array.Add(a);
array.Add(b);
Console.WriteLine(array.Count()); //2 Elements are in the array
}
}
//Sample element class.
public class Element{
public readonly String MyString;
public Element(String myString){
MyString = myString;
}
}
//Sample array class.
public class MyArray{
private readonly Element[] myArray;
private int count; //Use a property here
public MyArray(int size){
//Be careful -> check if size is >= 0.
myArray = new Element[size];
}
public bool Add(Element element){
if(myArray.Length == count) // return false if no more elements fit.
return false;
myArray[count] = element;
count++;
return true;
}
public int Count(){
return count;
}
}
So there is no need for creating an extra count loop. Your "count" variable in "MyArray" class holds always the correct value.
Anyway the implementation or use case of this code is a little bit clunky.
Why are you cant use directly a more safe list or something. That would be a better solution.
What do you need CBooks for? From what I understand, it's just an array of 8 CBook objects so why not use CBook[]?
CBook A1 = new CBook("Title1", "Author1");
CBook A2 = new CBook("Title1", "Author2");
CBooks[] ArrayOfBooks = new CBook[8];
ArrayOfBooks[0] = A1;
ArrayOfBooks[1] = A2;
int aux = 0;
for (int k = 0; k < ArrayOfBooks.Length; k++)
{
//break the loop because we know there are no more books
if (ArrayOfBooks[k] == null)
break;
aux++;
}
The question doesn't cover what the variables NumberOfTeams and Teams are for but could those be added to the implementation of CBook?
I have a for loop which invokes a method within it. I add the return of this method to a list however I am getting duplicates in the list (the last return from the method call is all the items in the list). Presumably this is because the result object is the same instance. Is there a way around this?
IList<CarResult> carResults = new List<CarResult>();
for (int i = 0; i < cars.Count(); i++)
{
result = calculation.RunForCar(
engineSize[i],
yearOfManufacture[i],
carResults.Add(result);
}
return carResults;
}
I'm going to make a qualified guess and try to explain what's going on, without knowing exactly what's happening in your RunForCar().
Presumably this is because the result object is the same instance.
Probably yes.
Here's an example. It will not create new instances of Foo, but re-use the same instance over and over. So every time the name changes it changes the name on the reference. The list itself only contains the references, and therefore all the items in the list will be changed if you change the name on the reference.
var list = new List<Foo>();
var result = new Foo();
for(int i = 0; i < 5; i++)
{
result.Name = i.ToString();
list.Add(result);
}
foreach (var foo in list)
{
Console.WriteLine(foo.Name);
}
Output:
4
4
4
4
4
If we instead do like the code below, we assign result to a new reference, and then we leave the existing references untouched.
var list = new List<Foo>();
var result = new Foo();
for(int i = 0; i < 5; i++)
{
result = new Foo()
{
Name = i.ToString()
};
result.Name = i.ToString();
list.Add(result);
}
foreach (var foo in list)
{
Console.WriteLine(foo.Name);
}
Output:
0
1
2
3
4
Is there a way around this?
Yes, you can simply create a new instance of result for every loop. Without knowing more about either CarResult or RunForCar I cannot say when it's best to create the new instance. But here's an example:
IList<CarResult> carResults = new List<CarResult>();
for (int i = 0; i < cars.Count(); i++)
{
result = new CarResult();
result = calculation.RunForCar(
engineSize[i],
yearOfManufacture[i]); // Fixed type-o?
carResults.Add(result);
}
return carResults;
Alternatively you can have a local variable inside the loop.
IList<CarResult> carResults = new List<CarResult>();
for (int i = 0; i < cars.Count(); i++)
{
var result = new CarResult(); // Will not be accessible outside of loop.
result = calculation.RunForCar(
engineSize[i],
yearOfManufacture[i]); // Fixed type-o?
carResults.Add(result);
}
return carResults;
If the result is the same instance, you need to replace IList with HashSet
You need to create a new instance or result object on every pass of the loop in order to avoid adding it by reference to carResults list. Oterwise, all items in carResults will hold the reference to the same object which will contain the data from the last loop cycle.
I have a loop:
List<A> list = new List<A>();
A obj = new A();
for (int i = 0; i < 10; ++i)
{
obj.num = i; // Assigns the current i to the num attribute inside obj
list.Add(obj);
}
However, in this case, whenever I modify obj, the previous instances of obj already added into the list will be modified. How do I write the code such that whatever that is added into the list has no more reference to the current value of obj?
You can create a new List<A> via Linq instead of adding:
List<A> list = Enumerable
.Range(0, 10)
.Select(i => new A() { num = i })
.ToList();
If you prefer adding
List<A> list = new List<A>();
for (int i = 0; i < 10; ++i)
list.Add(new A() {num = i}); // <- Adding new (copied) instance
you should move declaration of obj variable inside for loop
List<A> list = new List<A>();
for (int i = 0; i < 10; ++i)
{
A obj = new A();
obj.num = i; // Assigns the current i to the num attribute inside obj
list.Add(obj);
}
it is all just about variable scopes. here obj scope is inside a for loop iteration. if you want to use a variable between iterations you should define it out of for loop like the way you have declared obj before.
That's happening because probably A obj = new A(); is a ByReference object. So whenever you're in the loop, it's also changing the object you added on the List.
What you can do is either
Create the object inside the loop and then add it.
for (int i = 0; i < 10; ++i)
{
A obj = new A(); // create here so it's a new object always
obj.num = i; // Assigns the current i to the num attribute inside obj
list.Add(obj);
}
Make the A type IClonable
class A : ICloneable
{
public object Clone()
{
return this.MemberwiseClone();
}
}
and then just cast in before adding.
List<A> list = new List<A>();
A obj = new A();
obj.num = 0;
for (int i = obj.num; i < 10; ++i)
{
var clonedObj = obj.Clone() as A; // cast to be able to add in the collection
clonedObj.num = i;
list.Add(clonedObj);
}
I have a method which takes in N, the number of objects I want to create, and I need to return a list of N objects.
Currently I can do this with a simple loop:
private static IEnumerable<MyObj> Create(int count, string foo)
{
var myList = new List<MyObj>();
for (var i = 0; i < count; i++)
{
myList .Add(new MyObj
{
bar = foo
});
}
return myList;
}
And I'm wondering if there is another way, maybe with LINQ to create this list.
I've tried:
private static IEnumerable<MyObj> CreatePaxPriceTypes(int count, string foo)
{
var myList = new List<MyObj>(count);
return myList.Select(x => x = new MyObj
{
bar = foo
});
}
But this does seem to populate my list.
I tried changing the select to a foreach but its the same deal.
I realized that the list has the capacity of count and LINQ is not finding any elements to iterate.
myList.ForEach(x => x = new MyObj
{
bar = foo
});
Is there a correct LINQ operator to use to get this to work? Or should I just stick with the loop?
You can use the Range to create a sequence:
return Enumerable.Range(0, count).Select(x => new MyObj { bar = foo });
If you want to create a List, you'd have to ToList it.
Mind you though, it's (arguably) a non-obvious solution, so don't throw out the iterative way of creating the list just yet.
You could create generic helper methods, like so:
// Func<int, T>: The int parameter will be the index of each element being created.
public static IEnumerable<T> CreateSequence<T>(Func<int, T> elementCreator, int count)
{
if (elementCreator == null)
throw new ArgumentNullException("elementCreator");
for (int i = 0; i < count; ++i)
yield return (elementCreator(i));
}
public static IEnumerable<T> CreateSequence<T>(Func<T> elementCreator, int count)
{
if (elementCreator == null)
throw new ArgumentNullException("elementCreator");
for (int i = 0; i < count; ++i)
yield return (elementCreator());
}
Then you could use them like this:
int count = 100;
var strList = CreateSequence(index => index.ToString(), count).ToList();
string foo = "foo";
var myList = CreateSequence(() => new MyObj{ Bar = foo }, count).ToList();
You can Use Enumerable.Repeat
IEnumerable<MyObject> listOfMyObjetcs = Enumerable.Repeat(CreateMyObject, numberOfObjects);
For more info read https://msdn.microsoft.com/en-us/library/bb348899(v=vs.110).aspx