I am trying to implement a deep clone feature and already asked some question:
IsPrimitive doesn't include nullable primitive values
Array, List, IEnumerable, CustomList class cast to one and iterate threw them
https://stackoverflow.com/questions/20978951
I am modifing the following code:
https://stackoverflow.com/a/11308879/2598770
And currently I am facing the problem, that my original list also gets modified with "copied" objects instead of only the clone object, as recommended by the 2nd earlier question (Array, List, IEnumerable, CustomList class cast to one and iterate threw them). I have change the following code:
//Code directly from source
var cloneObject = CloneMethod.Invoke(originalObject, null);
if (typeToReflect.IsArray)
{
var arrayType = typeToReflect.GetElementType();
if (IsPrimitive(arrayType) == false)
{
Array clonedArray = (Array)cloneObject;
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
}
}
To a version that also handles, IList and not just arrays:
var cloneObject = CloneMethod.Invoke(originalObject, null);
if (cloneObject is IList)
{
if (typeToReflect.GetGenericArguments().Any())
{
var arrayType = typeToReflect.GenericTypeArguments[0];
if (IsPrimitive(arrayType) == false)
{
var clonedArray = (IList)cloneObject;
if (clonedArray.IsReadOnly == false)
for (var i = 0; i < clonedArray.Count; i++)
{
var originalListEntry = clonedArray[i];
var clonedListEntry = InternalCopy(originalListEntry, visited);
clonedArray[i] = clonedListEntry;
}
}
}
}
but on the line clonedArray[i] = clonedListEntry;, it doesnt just change the clonedArray but also the originalObject.
How can I prevent this, so that the clonedListEntry only gets set on the clonedArray?
You need to create a new container instead of just getting a reference to cloneObject. For example:
var existingList = (IList)cloneObject;
var clonedArray = Array.CreateInstance(arrayType, existingList.Count);
You can then go on to populate clonedArray. In the existing code the clonedArray is just a reference to your original list (it's not even guaranteed to be an array!), so any changes made to it get reflected to the original as well.
Related
I have a list that contains objects of type IMyClass.
This list contains instances of typed class MyClass<`T>.
I need to take two random objects from the list of the same MyClass type and perform an operation on them.
My code:
var item1 = list[random.Next(list.Count)];
...
var subset = list.OfType<MyClass<item1_T>>().ToList();
var item2 = subset[random.Next(subset.Count)];
What do I have to fill in at the dots to get the type item1_T?
Unfortunately in C# it is no possible to dynamically specify a generic type using the angle brackets notation as you wrote:
var subset = list.OfType<MyClass<item1_T>>().ToList();
Although you certainly could call OfType<> method using reflection:
var list = new List<IMyClass> { ... };
var item1_T = typeof(int);
var subset = (typeof(Enumerable)
.GetMethod(nameof(Enumerable.OfType))
.MakeGenericMethod(typeof(MyClass<>).MakeGenericType(item1_T))
.Invoke(null, new object[]{ list }) as IEnumerable<IMyClass>)
.ToList();
But that would be quite inefficient.
A much faster way would be:
var typeToFind = typeof(MyClass<>).MakeGenericType(item1_T);
var subset = list.Where(item => item != null && item.GetType() == typeToFind).ToList();
If this code is not performance critical, probably it wouldn't matter if you use this solution, otherwise I'd suggest to refactor the code to avoid using reflection.
In IMyClass add this line:
bool AreInterchangable(IMyClass otherInstance);
Implement in MyClass<`T>:
public bool AreInterchangable(IMyClass otherInstance)
{
return otherInstance is IMyClass<T>;
}
Instead of var subset = list.OfType<MyClass<item1_T>>().ToList(); use this:
var subset = list.Where(item => item.AreInterchangable(item1));
Note that I also introduced a generic interface IMyClass<T> which sits between IMyClass and MyClass<T>
"Source array was not long enough. Check srcIndex and length, and the array's lower bounds."
I have not mentioned any size while creating destination list (in where I'm adding object) and source list (from where I'm adding objects in destination list)
Here is similar code what I'm doing.
List<Obj> sourceList = respObj.List;
if (sourceList != null)
{
destinationList.AddRange(sourceList);
}
here sourceList is being returned from a method calling and there also I'm not mentioning any size or index while creating it.
it is as simple as this.
//destination list is globally declared, and initialized in constructor
public List<Obj> destinationList;
//Constructor
public Class()
{
destinationList = new List<Obj>();
}
List<Obj> Method()
{
List<Obj> sourceList = new List<Obj>();
foreach(Obj obj in AlreadyGeneratedObjList)
{
if(SatisfyingCondition(obj))
{
sourceList.Add(obj);
}
}
return sourceList;
}
Exception occurs on "AddRange()" method
I assume destinationList is declared as
var destinationList = [?]{}; // ? being some size
The problem with this is that your array can't resize automatically which is what is needed for .AddRange() to be able to add contents of sourceList into it.
The way to solve this would be to either declare destinationList as a list
var destinationList = new List<obj>{};
or as an array with a correct size, ie. size that would accommodate the size of sourceList after it has been filtered. This may not be the best way to go about it though.
My personal recommendation would be to use a List<>.
Also don't forget to check that the filtered sourceList is not empty or null.
Edit
You should consider changing this as follows
if (sourceList != null && sourceList.Count > 0)
{
destinationList.AddRange(sourceList);
}
else
{
//destinationList = sourceList;
// If the sourceList is ever null it will make every subsequent call
// destinationList fail with a NullReference exception.
destinationList.Clear();
}
I have the following method :
private static Tuple<List<int>, bool> GetTurns(List<int> possibleTurns, IList<int> currentLine)
{
List<int> localPossibleTurns = possibleTurns;
foreach (var passedTurn in passedTurns)
{
for (int j = 0; j < localPossibleTurns.Count; j++)
{
int indexOfNewNumber = currentLine.IndexOf(localPossibleTurns[j]);
if (localPossibleTurns[j] == passedTurn.Item1)
{
if (indexOfNewNumber + 1 == passedTurn.Item2 || indexOfNewNumber == passedTurn.Item2)
{
localPossibleTurns.RemoveAt(j);
}
}
}
}
return localPossibleTurns.Count == 0
? new Tuple<List<int>, bool>(localPossibleTurns, false)
: new Tuple<List<int>, bool>(localPossibleTurns, true);
}
and using it at this line :
if (!GetTurns(possibleRoutes, secondLine).Item2)
{
//do something
}
whenever it reaches that point it passes the possibleRoutes List into the method and since the list is reference type whenever a value is removed from the one declared in the method GetTurns - localPossibleTurns same happens to the possibleRoutes list. How can I avoid this and change the values of possibleRoutes only when I do possibleRoutes = GetTurns(possibleRoutes, secondLine).Item1; ?
Just assigning it to a new variable does not create a new list. If you want a copy you can use possibleTurns.ToList() or better new List<int>(possibleTurns). I prefer the latter for readability regarding object creation and because someday the might change the code of ToList() for performance gains to first check the type and then perform a simple cast.
public static List<T> ToList<T>(this IEnumerable<T> enumerable)
{
if (enumerable is List<T>)
return (List<T>) enumerable;
....
You are modifying the collection passed in as a parameter.
You can create a new collection to work with inside the method using Linq ToList:
List<int> localPossibleTurns = possibleTurns.ToList();
// Get list from DB
List<Category> dbCategories = DatabaseWrapper.GetCategories();
...
// COPY values to Obs.Col.
var shownCategries = new ObservableCollection<Category>(dbCategories);
// This also changes the value in 'dbCatagories'.
shownCategories[0].Name = "Hej";
I want to be able to change a value in the obs.col. without changing the same value in dbCategories. I can't figure out what I'm doing wrong since the 2nd line of code should be a copy constructor?
Nothing can explain more than source code. You're making invalid assumptions.
Namely:
private void CopyFrom(IEnumerable<T> collection)
{
IList<T> items = this.Items;
if (collection == null || items == null)
return;
foreach (T obj in collection)
items.Add(obj);
}
and
public ObservableCollection(List<T> list)
: base(list != null ? (IList<T>) new List<T>(list.Count) : (IList<T>) list)
{
this.CopyFrom((IEnumerable<T>) list);
}
as you can see, "copy constructor" indeed does make copy of LIST object itself, but it does not make copy of individual list elements. Note, it's not that simple, your example could actually work if Category would be struct.
This is usual behaviour, and it's called shallow copying. Alternatively, what you are looking is called deep copying
You need to create manually copies, either using serialization, or just create copy-constructor in Category class.
This would be good example:
new ObservableCollection(dbCategories.Select(x => new Category(x)).ToList());
I am trying to figure out a way that tells me if a certain type is an array/list/ienumerable/collection ... I dont care what kind of it is even CustomLists so something like
FooList<T> : IList<T>
FooList : IList
or stuff like that.
I kinda hoped that a simple type.IsArray would be enough but sadly this isnt the case.
I need a way to check if its one of the above types and then check what the underlying type is, and than cast it to a Indexed based collection, where I can loop through the entries.
For a simple array this is all I need:
if (obj.GetType().IsArray)
{
var elementType = obj.GetType().GetElementType();
if (elementType.IsPrimitive == false)
{
var array = (Array)obj;
}
}
This should work for every collection, there could possible be.
Edit:
As recommended below, I should as/is to IEnumerable but with IEnumerable I have the problem that the I cannot set certain object inside this IEnumerable.
With array I have used the method array.SetValue(obj, index) which works fine.
When I loop threw the IEnumerable and try to set one entry like this:
var list = obj as IEnumarble;
if (list != null)
{
foreach (var item in list)
{
item = new object();
}
}
I am getting the following message:
Readonly local variable cannot be used as an assignment target.
You can try to cast it with the as operator:
var enumerable = list as IEnumerable;
if (enumerable != null)
{
foreach (object item in enumerable)
{
// ...
}
}
However, if you need to modify it you have to recreate it. For example by using a list which you fill in the loop. Then reassign it to the original variable.
Or you could check if the type is a ILIst in the first place (like an array or list), then you can use it`s indexer:
var ilist = list as IList;
if (ilist != null)
{
for (int i = 0; i < ilist.Count; i++)
{
ilist[i] = "new value";
}
}
IIRC, you could do a simple inheritence check for the enumerable interface via
if (FooList is IEnumerable)
// We have a List
You can also use Linq and do a
if (FooList.ToList().Count > 1)
// We have a List
But this would be rather unconventional.