How might a List<T> be reset? - c#

I have an object I create and stick in a collection:
public class Level
{
private string id;
private List<LevelTypes> usableLevelTypes; // LevelTypes is an enum
private List<BlockTypes> levelMapping; // BlockTypes is also an enum
public Level(string id, List<LevelTypes> levelTypes, List<BlockTypes> incomingBlocks)
{
this.id = id;
this.usableLevelTypes = levelTypes
levelMapping = incomingBlocks;
}
Stepping through this, I can see each item being set properly. The object is then placed in a HashSet.
Another class then iterates through the HashSet calling each item's overloaded .ToString() method.
At this point I have all relevant variables in this class on my watch list. Everything within the object called is set properly. "id", "levelMapping" and all other variables that I have not listed including other List<T>'s and int's contain their proper values except "usableLevelTypes", which is reported as being empty.
public override string ToString()
{
var s = new StringBuilder();
s.Append("ID: " + id);
s.Append(" Level Types: " + usableLevelTypes[0].ToString()); // At this point,
// this list should have at minimum one value in it. However, it is empty and
// will throw an exception stating as much.
return s.ToString();
}
At no point is .Clear() called on the usableLevelTypes List and it is read-only. How could it be reset when other lists within the same object are not?

You are not creating a copy of the list that you pass in to the Level constructor for usableLevelTypes. So whatever is happening to that outer list is going to happen to the list inside Level. Without seeing the calling code, I cannot tell you specifically what the problem is.

Related

Is there a way to make a IEnumerable method that lets me return any list type in c#?

So currently, you can imagine I have 1 method that is the constructor that funcitons like
info.PersonalInfo=getPersonalInfo(Id);
info.MedicalInfo=getMedicalInfo(Id);
Thing is, all of those get data and get binarys are repeating 95% of the code
using (CVDataEntities data = new CVDataEntities())
{
var temp = data.PersonalInfo.Where(m => m.Id == Id).FirstOrDefault();
return temp;
}
The only thing that changes is instead of PersonalInfo its MedicalInfo.
I thought of using a switch and just sending a number as the selector for which specific object I would want.
But the problem is the method is made so that it can only return
public IEnumerable<PersonalInfo> getPersonalInfo (string Id)
Is there any way for me to make a IEnumerable that lets me return any object, or is there a better way to go about this. I want to do it mostly to reduce the code from 400 lines down to 200 at most.
Try using generic methods, you will be able to specify the return type of your function when you call it. This could make your code look like this :
public IEnumerable<T> getInfo<T>(string id)
{
// Some code
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id);
info.MedicalInfo = getInfo<MedicalInfo>(Id);
But be careful while using it, cause the compiler won't know what type T is (it is only defined at runtime) so it could lead to some errors while processing the data (like missing properties / methods exclusive to a specific type)
EDIT : Johnathan Barclay made a good point by commenting that the // some code bit is relevant and asked "How would the correct property be selected on data? How do you access an Id property on T?"
To get the correct property and access an Id property, you could use System.Reflection and add a string parameter to get the name of the property you want to use, and another to give the Id property name to the function:
public IEnumerable<T> getInfo<T>(string id, string propertyToReadName, string propertyToCompareName)
{
using (CVDataEntities data = new CVDataEntities())
{
// Getting the enumerable not filtered first
IEnumerable<T> unfilteredList = (IEnumerable<T>)data.GetType() // Get the type
.GetProperty(propertyToReadName, typeof(T)) // Get the property (PersonalInfo or MedicalInfo)
.GetValue(data); // Get the value of this property in the `data` instance
// Filtering the list
IEnumerable<T> filteredList = unfilteredList.Where(m =>
typeof(T).GetProperty(propertyToCompareName) // Get the "id" property using parameter
.GetValue(m) // Get the "id" value of m instance
.Equals(id)); // Check if it equals the id given as parameter
return filteredList;
}
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id, "PersonalInfo", "Id");
info.MedicalInfo = getInfo<MedicalInfo>(Id, "MedicalInfo", "Id");
If you want to return a single element instead of an IEnumerable don't forget to change the return type of the function from IEnumerable<T> to T and add .FirstOrDefault() at the return line
Note that you could also give another value to the parameter propertyToCompareName and make a comparison to another property of the T class

Changing one property in multidimensional array changes other array's properties too

I have a multidimensional array called SensorGetResult. In some cases this array can have a single array in it. If that happens, I must copy this one array and add it again, so I must have 2 arrays now. Then I need to change these array's dateTime property. This is my code:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
sensorGet.SensorGetResult[0].dateTime = end; //It works correctly including this line
sensorGet.SensorGetResult[1].dateTime = start; //At this line both array's dateTime property changes
Why can't I assign dateTime properties to each array individually?
It looks like you are using a reference type for your helperArray.
When the following code executes:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
What actually happens is you take a the first element of SensorGetResult which is a reference to the object (which I believe you intend to copy) and append the reference to the list thus resulting in a list which has two references to the same object in the memory.
If you want it to make a copy of the object, you have to implement that by yourself. Usually this means creating a new object of the same type and copying all the properties.
var objectToCopy = sensorGet.SensorGetResult[0];
var helperArray = new WhatEverTypeIsYourHelperArray {
Property1 = objectToCopy.Property1,
Property2 = objectToCopy.Property2,
// etc.
};
sensorGet.SensorGetResult.Add(helperArray);
But you have to be aware if any of the properties is furthermore a reference type, you need to do this recursively for all the properties.
If WhatEverTypeIsYourHelperArray is type you own, you could utilize Object.MemberwiseClone method and make it all easier for yourself. You can do this by implementing a method like the following. As a note, MemberwiseClone is a protected method hence the need of a new method in your class.
public WhatEverTypeIsYourHelperArray Clone() {
return (WhatEverTypeIsYourHelperArray)this.MemberWiseClone();
}
But even the MemberwiseClone() method doesn't copy reference types for you, rather just copies the pointers to the objects which means that all the properties of reference type of both the original and the cloned object will point to the same objects in the memory.
SensorGetResult row seems to be a reference type.
So when you wrote
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
you actually said that new row in SensorGetResult will point to the same object as the first one.
You can implement method like below:
public SensorGetResultRow Clone()
{
return new SensorGetResultRow (this.field1, this.field2, etc...)
//or if you use parameterless constructor
return new SensorGetResultRow ()
{
field1 = this.field1
//etc.
}
}
and use it:
var helperArray = sensorGet.SensorGetResult[0].Clone();

Initialize nested Object from a list

I have a list of integers (Levels). I want to initialize a nested Object of Filter called myFilter as below(Filter is a class with two properties: Value and NextFilter):
var myFilter = new Fliter{
Value = Levels[0],
NextFilter = new Filter{
Value = Levels[1],
NextFilter = new Filter{
Value = Levels[2],
NextFilter = new Filter{
Value = Levels[n],
NextFilter = null
}
}
}
}
Level's count is not static and depends on the input list (I have a multi select list that generates Level)
How can I do that?
This is a classic event for using - the technique of a method that calls itself:
public static Filter CreateFilter(List<int> values) => values.Any() ? new Filter //If the list contains elements, create the filter
{
Value = values.First(), //assign the first item of the values to the value property
NextFilter = CreateFilter(values.Skip(1).ToList()) //Create the rest of the nested object with the rest of the values
} : null; //If there aren't any items left in the list, return null and stop the recursion
You could of course do it in the constructor as well:
public Filter(List<int> values)
{
if (!values.Any()) return;
Value = values.First();
NextFilter = values.Count > 1 ? new Filter(values.Skip(1).ToList()) : null;
}
For more information about recursion, take a look at this: https://www.dotnetperls.com/recursion, for more information on nested classes read through this: https://www.dotnetperls.com/nested-class.
A few more information on recursion:
You can actually achieve everything through recursion - you don't even need loops. That's the reason why in languages like Haskell loops don't exist.
The simplest recursive function is:
public static void EndlessLoop()
{
//Loop body
EndlessLoop();
}
However, even Resharper suggests to convert it to a loop:
Another example, if you want to get the sum of a list you could do:
public static int Sum(List<int> summands) => summands.Count > 0
? summands.First() + Sum(summands.Skip(1).ToList())
: 0;
But those examples aren't useful in C#, as C# isn't a functional programming language, which causes recursion to be slower than loops. Furthermore recursion often causes a StackOverflowException (fitting to this site). If you run the endless loop recursion, it doesn't even take a second till your stack is full.
The reason for this is, that C# adds the address, from which a method got called, to the stack. If a method is called very often (and in 1 second a lot of recursive calls are made) a lot of addresses are added to the stack, so that it overflows.
However I still think, even though those examples aren't useful in c#, that it's quite useful to be able to handle recursion. Recursion is for example the only way to explore a directory structure, for getting for example all files:
public static List<FileInfo> GetAllFiles(DirectoryInfo directory) => directory.GetFiles()
.Concat(directory.GetDirectories().SelectMany(GetAllFiles))
.ToList();
And, as you experienced, it's the only way to fill a nested class from a list properly.
Just make a constructor of Filter, that will get Levels array as a parameter, that will set it's Value as level[0], and init NextFilter = new Filter(level.Skip(1)). Something like that. And it will recursively initialize your object.

C# type of variable. (variable)

I have an array of Items:
Item[] ItemsOut;
The problem is I have some derived types Item, like EquipItem. They are in this array too.
I have an AddItem function for both of the Item and EquipItem classes.
Inventory.AddItem(AllRecipes.Recipes[curPage].ItemsOut[i]......);
The idea is that in case my array is of type Item[] so it is always adding Items like normal Item.
My current solution is working and it's:
if (AllRecipes.Recipes[curPage].ItemsOut[i].GetType() == typeof(EquipItem))
{
Inventory.AddItem((EquipItem)AllRecipes.Recipes[curPage].ItemsOut[i], AllRecipes.Recipes[curPage].ItemsOut[i].number);
}
else
{
Inventory.AddItem(AllRecipes.Recipes[curPage].ItemsOut[i], AllRecipes.Recipes[curPage].ItemsOut[i].number);
}
But if I have 100500 child classes I will be forced to do 100500 if statements.
How to make this automatic? Something like:
Inventory.AddItem((AllRecipes.Recipes[curPage].ItemsOut[i].GetType())AllRecipes.Recipes[curPage].ItemsOut[i], AllRecipes.Recipes[curPage].ItemsOut[i].number);
or
Inventory.AddItem(AllRecipes.Recipes[curPage].ItemsOut[i] as AllRecipes.Recipes[curPage].ItemsOut[i].GetType(), AllRecipes.Recipes[curPage].ItemsOut[i].number);
I would like to be able to do something like:
Type t = AllRecipes.Recipes[curPage].ItemsOut[i].GetType();
Inventory.AddItem((t)AllRecipes.Recipes[curPage].ItemsOut[i].number);
But it causes a "variable used as a type" error, when I DO NEED to use it as a type. Not variable of course, but its value.
First off, unless you have two methods named AddItem (one for Item and another for EquipItem) you don't need to cast your ItemsOut, just use
Inventory.AddItem(AllRecipes.Recipes[curPage].ItemsOut[i], AllRecipes.Recipes[curPage].ItemsOut[i].number);
Second, you can't do this:
Type t = AllRecipes.Recipes[curPage].ItemsOut[i].GetType();
addItem((t)AllRecipes.Recipes[curPage].ItemsOut[i]
because t is an instance of the class Type, you can only cast using the type name like (EquipItem) AllRecipes.Recipes[curPage].ItemsOut[i]
And finally, unless you are having performance issues, don't ever try to optimize your code. Doing so is what we call Premature Optimization
So, check if you have a method AddItem(EquipItem equipItem). If you don't, you don't need to check it's type and cast the Item to EquipItem. Hope I could help you.
EDIT the best thing I can think that can improve your code is clearing it a bit, like this:
var item = AllRecipes.Recipes[curPage].ItemsOut[i];
if (item is EquipItem)
{
Inventory.AddItem((EquipItem) item, item.number);
}
else
{
Inventory.AddItem(item, item.number);
}
The idea here should be not to make anything know the difference between an Item, an EquipItem (or for that matter any old FooItem).
If they all derive from Item and the array is typed as Item[] then those subclasses can be added to the array without issue
var item = new Item();
var equip = new EquipItem(); // where EquipItem inherits from Item;
var array = new Item[]{ item,equipItem}; // no error here
If you have a single AddItem method, which adds to this array and performs some action based on the type, again the calling code should not know anything about the type - perhaps some virtual method which does some action when added to the list
public class Item
{
public virtual void AddedToList(){}
}
public class EquipItem
{
public override void AddedToList()
{
// behaviour specific to EquipItem
}
}

List item in another class disappears when removing as passed instance

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.

Categories