I have created a list of objects using a C# line like this:
gameObjects.Add(new Object());
I have also made a function that prints on the screen a list of the object types contained within gameObjects.
for(int i = 0; i < gameObjects.Count; i++)
Console.WriteLine(gameObjects[i].GetType());
So far so good. However I'm getting more items printed on the screen than should be present in gameObjects, so I've been trying to work out a way of finding out whether any of the entries are duplicates as I can't find anything in my code that could be creating extra objects in the list. It would be great if I could print out the names of each object in the list, but as I haven't given them names I don't think this is possible. Is there anything else that would differentiate one object in the list from another that I could take advantage of? As it's just debugging, I didn't really want to have to go in and make sure each object is given a name.
Thanks!
Edit:
For those asking for more code, I have a function that adds objects of type staticObject to the gameObjects list:
private void CreateStaticObject(Vector2 v2StaticObjectPosition)
{
Texture2D staticObjectTexture = Content.Load<Texture2D>(#"textures\StaticObject");
GameInfo.gameInfo.gameObjects.Add(new StaticObject(staticObjectTexture, v2StaticObjectPosition, sbSpriteBatch));
}
The list is contained within a class called GameInfo. Each StaticObject inherits from a Sprite class, if that's of importance.
I also add a Player object to the list, which inherits from the StaticObject class:
private void CreatePlayer(Vector2 v2PlayerPosition)
{
Texture2D playerTexture = Content.Load<Texture2D>(#"textures\Player1");
player1 = new Player(playerTexture, v2PlayerPosition, sbSpriteBatch);
}
I'm then printing out the contents of the list with this:
for(int i = 0; i < GameInfo.gameInfo.gameObjects.Count; i++)
{
string sObjectString = string.Format("Game object {0} is a {1}", i, GameInfo.gameInfo.gameObjects[i].GetType());
DrawWithShadow(sObjectString, new Vector2(10, 20 * i + 10));
}
DrawWithShadow() is just a simple method which nicely formats the text on the screen in the desired location. Unfortunately though, for each object that I create by calling the CreateStaticObject() method, I end up with two entries in my list.
Updated because I was dumb, ReferenceEquals checks reference equality.
Have you tried Object.ReferenceEquals()? It will tell you if reference a and reference b point to the same object.
var x = new object();
var y = x;
//This will print "true"
Console.WriteLine(Object.ReferenceEquals(x,y));
If you just want to filter the dupes out of the list, try this:
gameObjects = gameObjects.Distinct().ToList();
You can use GroupBy to find all duplicate objects by using reference equality:
var duplicateGroups = gameObjects.GroupBy(obj => obj).Where(g => g.Count() > 1);
Note that this will use the object's Equals + GetHashCode methods if they are overridden.
Thanks for all the suggestions guys. However I've discovered the source of my problem. I had an old line of code in the constructor for the StaticObject class which adds any created StaticObject to the list. So I was adding each object twice. D'oh! :S
Related
Here is the scenario
I know that class are reference types and Structures are value types
Below is Code1 which successfully outputs the Output1 which is expected functionality because as a new obj is created a new ref point is created and added to the persons list.
In code2. The same Object is getting assigned and as the code describes the foreach loop is actually updating the same reference that Obj is pointing all the time. At the end of for loop execution, final value is assigned to all the list items as in Output2
For case Code1 upon CAST tool review we are getting "Avoid object instantiation in loops".I know instantiation objects in for loop takes extra memory and performance too which is what I guess CAST tool is telling us. In such scenarios is there any solution that we can avoid new object instatiation inside the loop.
Using Structures is one solution based on the present scenario. But i would like to have any other ideas.
Code 1
public class person
{
public string name { get; set; }
public int age { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<person> personList = new List<person>();
for (int i = 0; i < 10; i++)
{
person Obj = Obj = new person();
Obj.name = "Robert" + i;
Obj.age = i * 10;
personList.Add(Obj);
}
foreach(person indv in personList)
{
Console.WriteLine(indv.name + indv.age);
}
}
}
Output
Robert00
Robert110
Robert220
Robert330
Robert440
Robert550
Robert660
Robert770
Robert880
Robert990
Code 2
List<person> personList = new List<person>();
person Obj = Obj = new person();
for (int i = 0; i < 10; i++)
{
Obj.name = "Robert" + i;
Obj.age = i * 10;
personList.Add(Obj);
}
foreach(person indv in personList)
{
Console.WriteLine(indv.name + indv.age);
}
Output 2
Robert990
Robert990
Robert990
Robert990
Robert990
Robert990
Robert990
Robert990
Robert990
I know instantiation objects in for loop takes extra memory and performance too which is what I guess CAST tool is telling us.
That's incorrect. An allocation will have the same "price" when used inside or outside that loop. I'm assuming your tool is warning you because allocating objects in a loop on each iteration may cause alot of objects to be instansiated, but that's exactly what's needed here. There is absolutely no need to avoid object allocation in this case.
I'd be more worried about that particular tool you're using and the advice it brings.
There is nothing wrong with instantiating those objects so I can't think why your tool is telling you that. At the end of the day the whole point of your code is to create a list of "person" objects. Whether you did it in a loop, or typed out all 10 instantiations in a row, it wouldn't make a difference. The loop is obviously better.
On another note though, you can really simplify this code by using linq, try writing it this way and see if your tool gives you the same warning:
List<person> personList = Enumerable.Range(1, 9).Select(x =>
new person { name = "Robert" + x, age = x * 10 }).ToList();
I mainly avoid instantiation in loops in cases where I want to use the object outside of the loop and it isn't being added to a collection. Additionally it wouldn't be necessary if I were instantiating in the loop and passing the object to another method within the loop. In that case you can instantiate outside of the loop and pass the values to the method within the loop. If this is all of the code that you're going to use, move the Console.WriteLine inside of the loop and don't bother instantiating inside of the loop.
But I get the impression that you're trying to create a collection of objects inside a loop to be used outside of that loop. In that case, your collection of objects isn't going to have any bigger memory footprint simply because you instantiated the objects inside the loop. As you can see, if you instantiate the object outside of the loop, you're simply assigning a new value to the same object and then adding a reference to the same object multiple times to the array. You'll need to instantiate each object in the array no matter what you do.
I'm currently freakin out a little.
I'm passing a List to a method of another class and in that class I'm using a different variable name (encapsulated). But here it is:
When I remove an item from the list within the method the item disappears in the other variable as well!
Any suggetions what I'm doing wrong?
Here the code snippet:
public partial class Form1 : Form
{
List<Vector> polygonPoints = new List<Vector>();
private void panel1_Paint(object sender, PaintEventArgs e)
{
// Create Convex Hull of polygon Set
QuickHull qh = new QuickHull();
// here I pass the list to a method in the class QuickHull
// here polygonPoints.Count = 5
List<Vector> hullOfPoints = qh.quickHull(polygonPoints);
// at this point I get polygonPoints.Count = 3
...
}
}
different class QuickHull:
class QuickHull
{
public List<Vector> quickHull(List<Vector> points)
{
List<Vector> convexHull = new List<Vector>();
...
Vector A = points[minPoint];
Vector B = points[maxPoint];
convexHull.Add(A);
convexHull.Add(B);
// at this point 'polygonPoints' also looses these items
points.Remove(A);
points.Remove(B);
...
}
}
I really don't know what to do because this was working all the time but from one moment to the other its not working anymore.
I'd really appreciate every suggetion.
Thanks in advance.
When you pass a List<T> to a method, you're passing a value which contains a reference to that list. That means that the argument you accept in your method, points, is pointing to the same list you instantiated higher up in the call chain.
If you want to pass a reference to a separate list, you'll need to create a new one:
List<Vector> hullOfPoints = qh.quickHull(polygonPoints.ToList());
You can read more on that in "Passing Reference-Type Parameters":
A variable of a reference type does not contain its data directly; it
contains a reference to its data. When you pass a reference-type
parameter by value, it is possible to change the data pointed to by
the reference, such as the value of a class member. However, you
cannot change the value of the reference itself;
Your problem is that you pass a 'reference' type and then change it. Instead you can create a NEW list (points) to avoid modifications of the previous input list (polygonPoints).
What you see is the expected behaviour.
A List<T> is a reference type, so when you pass it to a method it's the reference to the list that is passed.
Using a different variable name doesn't make it a new list. It's still the same list that you are referencing.
If you want a local copy of the list, you need to create a new list and copy the items into it. You can use the list constructor for this:
List<Vector> local = new List<Vector>(points);
You can also change the type that you send into the method:
public List<Vector> quickHull(IEnumerable<Vector> points)
By using the IEnumerable<T> interface instead of the List<T> class, you limit the use to only enumerating the list. You can still send a list into the method, but you can't change the list by mistake, and you can still use it to create the local copy.
(I've done as much as possible search based on keywords of "removeall where" or "removeall two argument predicate" without much luck so here goes)
The problem is I have a list of objects (of Class Wave) and a relationship function as:
private bool AinB(Wave A, Wave B), returning true if A 'is in' B. Also AinB(x,y) is true guarantees AinB(y,x) is false.
What's the best way to remove all of the objects in the list where the objects 'is in' another object in the list? i.e., after the removal, the list should only contain objects where neither are in the 'is in' relationship with any other object in the list?
ideally this can be done easily as a
listX.RemoveAll( (x,y) => AinB(x,y)) but of course this is not legal in C#, also there's no easy way to specify which to remove, x or y.
I thought about looping through the list with an index
int i = listX.Count - 1;
while (i>=0)
{
int r = listX.RemoveAll(X => AinB(X, listX[i]));
i = i - r - 1;
}
This seems to work, but I am wondering if there's better way with straight linq code to solve the problem.
Thanks.
Unfortunately I can't think of any way to do this that's not at least O(n^2). But the good news is that it's not that hard from a LINQ perspective:
listX.RemoveAll(item => listX.Any(isin => AinB(item, isin)));
Use a normal for loop that inspects the highest element first down to the lowest element in the list. Inspect the element at the current position for any duplicates within the list, if found remove the current element (and possibly decrement your iterator).
Example:
List<string> stuff = new List<string>(); //full of stuff
for(int i = stuff.Count - 1; i > 0; i--)
{
//Edited here for more efficiency.
for (int x = i - 1; x > 0; x--)
{
if (stuff[x] == stuff[i])
{
stuff.RemoveAt(i);
break; //or possibly continue;
}
}
}
This was hand-coded here so it might have a few syntactical errors, feel free to shoot me an edit if you find something's not quite right.
If you're a wizard with LINQ you could also try grouping the objects in the list and then just selecting the first object in each group for your output list..
you can use the LINQ Except call,
List a = new List();
a.Add("a");
a.Add("b");
a.Add("c");
List b = new List();
b.Add("b");
b.Add("c");
b.Add("d");
List c = a.Except(b);
list c will contain only item "a";
you can even make it more clever by giving a compare object,
List c = a.Except(b, new CompareObject());
I have quite a few lines of code that create objects and using various parameters with similar object names and constructors. The only thing that is changing is the actual name of the object variable being created, and the name of the objects themselves being passed in. Here is an example of code that matches my current setup:
BackyardObject backyardObject0 = new BackyardObject(cat0, dog0, goat0, piglet0);
BackyardObject backyardObject1 = new BackyardObject(cat1, dog1, goat1, piglet1);
BackyardObject backyardObject2 = new BackyardObject(cat2, dog2, goat2, piglet2);
BackyardObject backyardObject3 = new BackyardObject(cat3, dog3, goat3, piglet3);
BackyardObject backyardObject4 = new BackyardObject(cat4, dog4, goat4, piglet4);
// many many more BackyardObjects being instantiated
As we can see, the names of the object being created match exactly the names of the objects being passed into the constructor. Is it possible to create a loop that would set all this up?
Edit
I think I might have lacked details that were needed to get a correct answer for this question. It isn't a question on "how-to" use a loop, or how to add items to a collection it's more of a question to determine if is it possible to create a "variable name" dynamically inside of a loop, while accessing another variable name dynamically inside of the loop provided the information given above (just made up code on the spot).
// psuedo code for something I'm asking is possible
for( i = 0; i < 10; i++)
{
// create BackyardObject with generic name, while appending "i" to
// variable name, and accessing other object variables
BackyardObject backyardObject'i'
= new backyardObject(cat'i', dog'i', goat'i', piglet'i');
}
While I understand that I could create additional arrays and lists to store objects and then use those, I was just seeing if it was possible to get variable names dynamically. I wasn't sure if it was entirely possible, that's why I asked the question. I know that this is a strange question, but got curious after I seen this Objective-C code:
// Getting an arrayName dynamically based on loop index
for (int i = 0; i < 10; i++)
{
NSString *arrayName = [[NSString alloc] initWithFormat:#"column%d",
i];
NSArray *array = [self valueForKey:arrayName];
}
Objects don't have names... what you're talking about is variables. And rather than having lots of variables with numbers as suffixes, you'd be better using collections - arrays, or lists, or whatever. Then you can do:
// Or use arrays...
List<Cat> cats = new List<Cat>();
cats.Add(new Cat(...)); // Add the cats however you want to set them up
// Ditto dogs, goats etc
List<BackyardObject> backyardObjects = new List<BackyardObject>();
for (int i = 0; i < 10; i++)
{
backyardObjects.Add(new BackyardObject(cats[i], dogs[i],
goats[i], piglets[i]));
}
I assume you're fairly new to C# - I suggest you look at arrays and collections in whatever you're learning from.
I'm trying to create a small proof-of-concept application for my boss, however the code I've created that simulates what he's trying to pull off isn't working.
for (int i = 0; i < 5000; i++)
{
((IList<string>) obj2.Stuff).Add("Iteration " + i.ToString());
}
I'm trying to pull this off all in one line because this is what his code looked like the other day in the framework we're working on. Anywho when the code above executes, I get a runtime error saying "Collection was of a fixed-size". And when I try casting to a List instead of an IList, I get an InvalidCastException saying "Unable to cast object of type 'System.String[]' to type 'System.Collections.Generic.List`1[System.String]'."
Anybody have any ideas on how I can pull off a single-line cast to add an item to the IEnumerable or help me figure out a way around the two errors I keep getting? Thanks in advance.
EDIT (4/19/2011 10:49AM EST)
I'm adding more code to help people out -- probably should've done this earlier. Sorry.
Program.cs:
#region Cast Test
Class1 obj2 = new Class1();
obj2.Stuff = Enumerable.Empty<string>();
Console.WriteLine("Cast - Start Time: " + 0 + "ms");
Stopwatch stopwatch2 = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 5000; i++)
{
((IList<string>) obj2.Stuff).Add("Iteration " + i.ToString());
}
stopwatch2.Stop();
Console.WriteLine("Cast - Stop Time: " + stopwatch2.ElapsedMilliseconds.ToString() + "ms");
#endregion
Class1.cs:
public class Class1
{
private IEnumerable<string> stuff;
public IEnumerable<string> Stuff
{
get { return stuff; }
set { stuff = value; }
}
}
Arrays in C# are of fixed size. You can't add items to them.
You cannot cast a string[] to IList<string> because string[] does not implement that interface. You will need to create an object implementing IList<string> first:
List<string> list = obj2.Stuff.ToList();
for (int i = 0; i < 5000; i++)
{
list.Add("Iteration " + i.ToString());
}
Why cast inside the for loop? While every IList is an IEnumerable, not every IEnumerable is an IList. You can use the ToList extention method to copy the IEnumerable to a new List. (Just be sure to be using System.Linq;)
Also, in this example, there is no need for a for loop:
var list = obj2.Stuff.ToList();
new list.AddRange(Enumerable.Range(0, 5000).Select(i => "Iteration " + i.ToString()));
In comments on one of the answers above you say "I'm trying to avoid copying the list."
You are trying to add data to an object whose underlying type is IEnumerable<T>. But an IEnumerable object is not an actual container, it's an interface. You can't add stuff to an interface. But you can assign another object that implements that interface to it.
So to use foson's example above, you could just do:
obj2.Stuff = Enumerable.Range(0, 5000).Select(i => "Iteration " + i.ToString());
Note that when this code executes, nothing actually happens. No objects will be created until something actually iterates over obj2.Stuff. When that happens, the methods in LINQ will be called that create objects one at a time and return them to the iterator loop.
But there's no actual storage device involved here. You can iterate over obj2.Stuff and unless you consequently added each integer to something else, they would be gone at the next iteration.
The fundamental point here is you can't store things in IEnumerable, rather, IEnumerable is a way to return objects in sequence, and that could be from a list construct, or from a function that generates a sequence.
You can't call Add since obj2.Stuff seems to be an array, and an array has a fixed size, as the error message indicates.
The type System.String[] is an array of strings, which implements IEnumerable<string> but is not a List<string>. Arrays are of fixed length. Why do you need it in one line?
Change the type of obj2.Stuff to something like StringCollection or List . In .NET arrays are of fixed length and you can't add new objects to them, so use collections instead.
An IEnumerable does not give you the same features that an IList. Unless your Stuff is an IEnumerable that is also an IList your code won't work.
The contract of the first only promises that you can iterate it one element at a time. It never states that you can add new elements.
You can only pull this off if and only if at runtime your enumerable object also satisfies the IList interface.
As obj2.Stuff is a string array, it's not possible to do what you are trying to do. An array can't be resized, so you can't add items to it.
If you can replace the array in obj2.Stuff with another array, you can get the data from the array and make a list from it, add items to the list, get the data from the list and create an array from it, and use that to replace the original array:
List<string> items = new List<string>(obj2.Stuff);
for (int i = 0; i < 5000; i++) {
items.Add("Iteration " + i.ToString());
}
obj2.Stuff = items.ToArray();
If the obj2.Stuff property is read-only, then you can't add anything to it unless you change then underlying implementation from an array to a collection that you can add items to.