I noticed an issue with enumerating over a collection that was a result of a .Select<T>() I can enumerate over it, and modify each item in the collection. When I look at the collection after the enumeration is completed each item is left un-modified.
public class FooModel
{
public Guid? Id { get; set; }
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
IEnumerable<FooModel> foos = Enumerable.Range(0, 100)
.Select(m => new FooModel());
foreach(FooModel f in foos)
{
f.Id = Guid.NewGuid();
}
Assert.IsTrue(foos.All(foo => foo.Id.HasValue));
}
}
If I append .ToList() or .ToArray() to execute the enumerable, then it modifies the items within the collection.
I understand that the IEnumerable isn't executed after the .Select<T>, but when the foreach runs it creates an enumerator and executes the IEnumerable on the foo local field. Why isn't it referencing the same object that the Select<T> creates?
I've seen questions on when to use IEnumerable over List but I understand the difference between the two. Describing vs implementing more or less. My question is more in regards to why the foreach over a local field, executes the IEnumerable and doesn't operate on the same objects referenced in the local variable.
Why isn't it referencing the same object that the Select creates?
Because foo is using Enumerable.Range which enumerates on-the-fly. There is no underlying data store that you are iterating over.
When you call foreach you are executing the underlying query, which creates 100 FooModel objects on the fly, which you modify.
When you call .All, you execute the query again, which creates another 100 Foo objects on the fly that have not been modified.
When you hydrate the results via ToList or ToArray, you are then looping over a concrete collection, and your changes to the underlying objects will persist.
Related
I have a class with a static list as shown below:
public class Context
{
private static readonly List<Definition> definitions;
static Context()
{
definitions = LoadXML("path-to-xml-file.xml"));
}
public static List<Definition> GetDefinitions()
{
return definitions;
}
}
My problem is making calls to GetDefinitions() seems to return the list by reference instead of by value, because when I do this elsewhere in my code:
var defs = Context.GetDefinitions().ToList();
defs.ForEach(a =>
{
a.Name = a.Alias ?? a.Name;
});
all subsequent calls to Context.GetDefinitions() will return the modified list - not the original one, hence my conclusion that defs is not a value but a reference to the definitions list.
I tried adding the .ToList() in an attempt to decouple the reference but still I get the same result.
I am also open to a workaround, which allows me to use .Select() instead of .ForEach() in my sample code.
The problem is that the list does not store the items itself, but rather references to the items. Even if you create a new list (e.g. with ToList()), the referenced items stay the same.
In order to fix this, you need to clone the items in the list so that you have a independent copy of the data. You can implement ICloneable on the items and use return the list like this:
public static List<Definition> GetDefinitions()
{
return definitions.Select(x => (Definition)x.Clone()).ToList();
}
This way you create a new list that contains the cloned items. However, cloning an item is a tedious task if you need to clone a deeply nested class structure. For a flat structure, using MemberwiseClone is an easy way.
I have the following code which is called every 3 seconds continuously from a thread
public class SomeClass
{
List<Person> _list;
public SetList(List<Preson> list)
{
_list = list;
}
private void WorkToBeDoneEverythreeSeconds()
{
var filteredList= _list.Where(x= x.IsConditionValid());
//................Use the filtered list here.........
}
}
_list is a reference to a C# List owned by another class passed into this class. The list is updated from a different thread in its owner class. Sometimes updates happening while the Where clause is executed and Invalid operation is thrown.
What is the most efficient way to get a snapshot on the actual list when using the enumerator? I can think of creating another collection from the current collection, but doing this every 3 seconds might not be the best idea?
I have a list of classes.
For Example:
public class Object()
{
public string a;
public string b;
}
public List<Object> Objects = new list<Object>();
What I want is a Method that can set the string a in each Object in the List to Object[0].a;
And I want to do this to b and a lot of other vars too with a single Method.
(Other var types to, with only one Method)
is this possible?
Let's say you want a list of ten of those Object (unfortunately named) class instances in a list, and you want them all to have the a property set to "Foo". You can use Enumerable.Range to create an enumeration with ten elements, then Select to create the objects. When creating the object, you can initialize the a property using initializer syntax. Once they are created you can create the list with ToList.
Technically this is one line of code although for readability it may be a good idea to span it across a few visual lines.
List<Object> myList = Enumerable
.Range(0,10)
.Select
(
i => new Object { a = "Foo" }
)
.ToList();
Assuming the following piece of code that caches two collections of objects MyObject: one collection is of type IEnumerable<MyObject> and the other one is of type List<MyObject>. The code retrieves the values from the cache and then accesses the collection:
class Program
{
static void Main(string[] args)
{
CacheManager.CacheSomething();
}
public class MyService
{
private IEnumerable<AnObject> AnObjects
{
get
{
return new[]
{
new AnObject {MyString1 = "one", MyString2 = "two"},
new AnObject {MyString1 = "three", MyString2 = "four"}
};
}
}
public IEnumerable<AnObject> GetEnumerable()
{
return AnObjects;
}
public List<AnObject> GetList()
{
// Run it out to a list
return AnObjects.ToList();
}
}
public static class CacheManager
{
public static void CacheSomething()
{
// Get service
var service = new MyService();
// Get the values as List and Enumerable
var list = service.GetList();
var enumerable = service.GetEnumerable();
// Putting them in a cache
HttpRuntime.Cache.Insert("list", list);
HttpRuntime.Cache.Insert("enumerable", enumerable);
// Get the values
var retrievedList = HttpRuntime.Cache["list"] as List<AnObject>;
var retrievedEnumerable = HttpRuntime.Cache["enumerable"] as IEnumerable<AnObject>;
// Access both
var res1 = retrievedList.ToList();
var res2 = retrievedEnumerable.ToList();
}
}
public class AnObject
{
public string MyString1 { get; set; }
public string MyString2 { get; set; }
}
}
Is there a difference in terms of the amount of memory required to store these objects based on the collection types?
The reason that I ask is that when we have been profiling our applications and we've noticed that when we look at the dependency tree, the IEnumerable has the Service associated with it. Does that mean that it caches the service too?
Can anyone shed some light as to whether this is a cause for concern? Is it a problem to store an IEnumerable in the cache? Should we prefer caching Lists over IEnumerables?
And IEnumerable is not data. It is a promise that you will receive data when you ask. Some data might implement it (arrays, lists) but sometimes, it's not materialized data but instead a query to a database.
"Caching" your IEnumerable means you cache the knowledge where to get data. That's not what you want. You want to cache the data itself.
Always materialize your IEnumerables before caching the result (for example with ToList or ToArray). Otherwise, you might end up with a cache that just holds a procedure that is called to get you the data.
The fact that the IEnumerable in your example still holds a reference to the service is exactly that: it does not hold data, it holds the reference to the service and will call it again when you use it. So the exact opposite of what you want from the cache.
Difference in amount of memory? No.
Why? IEnumerable is not a type; it's an interface. That means that anything stored in an IEnumerable is actually some other type that implements IEnumerable (like List, for example).
IEnumerable only forces implementation of methods to read the list. It's not appropriate for modifying the collection. That's why you would want to cast it to the actual type (like List), so you can use methods like Add.
I have created a derived collection object to introduce some added functionality to filter the active records in the collection as shown in the below code snippet. How to achieve it as i want to just filter the same collection while keeping the original references in the filter without creating copy.
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes Active
{
get { return this.Where(x => x.IsActive).ToList(); } // Compile Error
}
}
Filtering an existing list
You mentioned that you wanted to just filter the existing list without keeping a copy. In this case, creating a List won't do, since creating a list from the subset will always create a new collection, not just a filter. List<T> is not a lazily-evaluated collection.
What you probably need to do is either define Active as IEnumerable<ExtendedType> and return the result of the Where directly (using LINQ's lazy implementation), or, if you're in WPF, use something like CollectionView as an additional filter on top of a collection, like this:
public ICollectionView ActiveTypes
{
get
{
if (_activeTypes == null)
{
_activeTypes = CollectionViewSource.GetDefaultView(myExtendedTypes);
_activeTypes.Filter = (type) => (type as ExtendedType).IsActive;
}
return _activeTypes;
}
}
You can now bind to ActiveTypes and get only a subset of the original list, filtered by the result of the Filter clause.
Creating a new List
However, assuming ExtendedType is a Reference type, you don't have to worry about copies of the items themselves being made by duplicating the list. If you don't mind creating a copy of the list with the same references, use my original answer:
The compiler is correct, in the sense that an ExtendedTypes is-a List<ExtendedType>, but not the other way around, and ToList() create a List<ExtendedType>.
There is, however, a simple workaround. Rather than ToList, just create a new ExtendedTypes with a constructor that initializes from a collection:
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes (IEnumerable<ExtendedType> items) : base(items)
{}
public ExtendedTypes Active
{
get { return new ExtendedTypes(this.Where(x => x.IsActive)); }
}
}