Add item to a list, without effect - c#

In a C# class I have a list and two different getters for the list:
private List<A> a;
public List<A> EveryA
{
get
{
if (a == null) a = new List<A>();
return a;
}
}
public List<A> FilteredA
{
get
{
return EveryA.FindAll(a => a.IsInFilter);
}
}
Now my question is: how about the syntax FilteredA.Add(this);?
It compiles and runs but it cannot add any item to any list.
Should a better compiler have to notify the (small) problem?

They are not the same list. This is not something the compiler can check for you, since the compiler can't really read your mind. Check the documentation for List<T>.FindAll
The result is a list, but it isn't the same list (how could it be? your original list isn't filtered!).
You should be able to add items to the list returned by FilteredA, except they won't show up in a.
I suggest you use LINQs Where instead, returning an IEnumerable<T>. That way, it is obvious that the result of FilteredA shouldn't be changed, only iterated over:
public IEnumerable<A> FilteredA
{
get { return EveryA.Where(a => a.IsInFilter); }
}

No. Why should it notify you about this? It is completely ok.
FilteredA doesn't return a but a new instance of a List<A>.
FilteredA.Add(this); adds this to this new instance.
See this code:
var filteredA = FilteredA;
int count1 = filteredA.Count;
filteredA.Add(this);
int count2 = filteredA.Count;
Assert.AreEqual(count1 + 1, count2);
This shows, that the new item IS added to the list. But to that new instance that is independent of the list inside your class.

FindAll returns a new list. You're adding the new item to the new list but not retaining a reference to the new list, I suppose. The semantics would be clearer if the filtered list came from a method rather than a property.

public List<A> FilteredA returns some output of the FindAll method, as a List<A>. This will not be the same object as EveryA so when it goes out of scope your addition will be lost.

It's not really a compiler issue - since the code is valid it will compile just fine. The problem is more on a code quality level. To catch something like this, you could use a tool like FxCop to analyze your code.
Both methods can be seen as query methods. You should not expose the result as a List, but rather an IEnumerable or A[]. If you want to add an item to the list, do so with an Add method.
private List<A> items = new List<A>();
public IEnumerable<A> EveryA
{
get { return items; }
}
public IEnumerable<A> FilteredA
{
get { return items.Where(item => item.IsInFilter); }
}
public void AddItem(A item)
{
items.Add(item);
}

Related

Inheritance and return types [duplicate]

public interface IDic
{
int Id { get; set; }
string Name { get; set; }
}
public class Client : IDic
{
}
How can I cast List<Client> to List<IDic>?
You can't cast it (preserving reference identity) - that would be unsafe. For example:
public interface IFruit {}
public class Apple : IFruit {}
public class Banana : IFruit {}
...
List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());
// Eek - it's a banana!
Apple apple = apples[0];
Now you can convert a List<Apple> to an IEnumerable<IFruit> in .NET 4 / C# 4 due to covariance, but if you want a List<IFruit> you'd have to create a new list. For example:
// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();
// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();
But this is not the same as casting the original list - because now there are two separate lists. This is safe, but you need to understand that changes made to one list won't be seen in the other list. (Modifications to the objects that the lists refer to will be seen, of course.)
A Cast iterator and .ToList():
List<IDic> casted = input.Cast<IDic>().ToList() will do the trick.
Originally I said covariance would work - but as Jon has rightly pointed out; no it won't!
And originally I also stupidly left off the ToList() call
I too had this problem and after reading Jon Skeet's answer I modified my code from using List<T> to use IEnumerable<T>. Although this does not answer the OP's original question of How can I cast List<Client> to List<IDic>, it does avoid the need to do so and thus may be helpful to others who encounter this issue. This of course assumes that the code that requires the use of List<IDic> is under your control.
E.g.:
public void ProcessIDic(IEnumerable<IDic> sequence)
{
// Implementation
}
Instead of:
public void ProcessIDic(List<IDic> list)
{
// Implementation
}
If you can use LINQ then you can do this...
List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Its only possible by creating new List<IDic> and transfering all elements.
In .Net 3.5, you can do the following:
List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());
The constructor for List in this case takes an IEnumerable.
list though is only convertible to IEnumerable. Even though myObj may be convertible to ISomeInterface the type IEnumerable is not convertible to IEnumerable.
OfType
You can try something like:
using (var dbContext = YourDatabaseContext())
{
var list = dbContext.Clients.Where(x => x.Happy)
.OfType<IDic>()
.ToList();
}
See https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.oftype
If you want to process the original list without creating a separated reference, you could define the generic method like this:
public void DoIterate<T>(List<T> myCollection) where T : IDic
{
foreach (T item in myCollection)
{
//update a property of interface
item.Name = "new Name";
}
}
Calling this method above to process the list without having to cast specific object to interface:
List<Client> clients = new List<Client>();
DoIterate(clients);
If you don't need to modify the contents of the original list, you can implicitly convert a List into a IReadOnlyList which will let you iterate over it's contents as IDics without creating a new list.
List<Client> myClients = new List<Client>();
myClients.Add(new Client());
IReadOnlyList<IDic> castedClients = myClients;
foreach(IDic val in castedClients)
{
//do something;
}
The conversion can also occur while simply returning the list like so :
public IReadOnlyList<IDic> getClientsAsIDic()
{
return myClients;
}

C# Modification of IEnumerable mystery, what is modifying my IEnumerable?

I've written an extension method to add items to an (EF) EntityCollection. I got an interesting error, saying that my collection of IEnumerable ("items") had been modified, after the first loop in the foreach. When I turn items into items.ToList() (like in the code below), it works fine.
I completely understand that doing ToList() will produce a copy of the items on which the foreach will then operate.
What I do NOT understand is what is modifying the IEnumerable when I'm doing a foreach over it.
Update: Somehow, it seems the items variable is the same as the collections variable?
Update 2: I think collection and entity may be affected by EF's entity tracking, but I still fail to understand why
Usage:
ssp.ServiceAreas.ReplaceCollection(model.ServiceAreas);
Here's my extension method:
public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, IEnumerable<TEntity> items)
where TEntity : EntityObject, IProjectIdentity<int>, new()
{
foreach (var item in items.ToList())
collection.AddOrUpdate(item);
}
public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, TEntity item)
where TEntity : EntityObject, IProjectIdentity<int>, new()
{
if (item.ID > 0 && collection.Any(c => c.ID == item.ID))
collection.Remove(collection.First(c => c.ID == item.ID));
// For now, the Remove NEVER gets hit
collection.Add(item);
}
collection.Remove(collection.First(c => c.ID == item.ID));
you are removing in the collection you are iterating.
I created the following sample code:
internal class Program
{
private static void Main(string[] args)
{
var one = new List<string> {"Adam", "John"};
var two = new List<string> {"Adam", "Houldsworth"};
one.AddOrUpdate(two);
Console.Read();
}
}
static class Extensions
{
public static void AddOrUpdate(this IList<string> collection, IEnumerable<string> items)
{
foreach (var item in items.ToList())
collection.AddOrUpdate2(item);
}
public static void AddOrUpdate2(this IList<string> collection, string item)
{
if (collection.Any(c => c == item))
collection.Remove(collection.First(c => c == item));
collection.Add(item);
}
}
This works as you would expect, there are no errors. So in essence, none of the lines are causing issues.
What will cause issues is if you call the list on itself:
one.AddOrUpdate(one);
So from what I can see, you must be calling this extension method with the same collection as both arguments.
If you are, then both Remove or Add will mutate the collection and cause this exception.
Perhaps EntityCollection doesn't like when someone takes over it's elements? So, when you Add to collection, item gets Removed from items.
Or it could be that items == collection
It could be that the first item is always a new item, and thus by default the ID is set to some initial value. It will then be added to the collection, letting the EntityFramework generate a new ID and assign it to the first added item.
It then might be that the EntityCollection thinks it has changed, because it uses the ID to sort or do something else internally. And thus the foreach operation (which is probably using the same list) throws the exception. That's also why the test-case proved by Adam Houldsworth does not give the issue!
EntityCollection<Customer> customers = new EntityCollection<Customer>();
Customer newCustomer = new Customer() {ID = 0};
customers.Add(newCustomer);
customers.AddOrUpdate(customers);

Linq - select where ancestors contain this?

sometimes, i just feel dumb...
i have a simple class:
public class myClass
{
public long Id { get; set; }
public long ParentChannelId { get; set; }
}
and i have a list that contains the class:
List<myClass> myItems = new List<myClass>
further down the code, i feed the list with classes.
now, i want to delete an item from the list.
but, since an item can have children and grandchilds etc...
i want to delete everything related..
was thinking of something like:
(pseudo code )
var List<myClass> itemsToDelete = myItems.Where(i => i.Ancestors.Contains(myItemId));
but i dont really have the brains atm to know how to write it exactly... :\
i do have the .Ancestors function...
just need help with the lambda linq
public List<Channel> Ancestors
{
get
{
List<MyCms.Content.Channels.Channel> result = new List<MyCms.Content.Channels.Channel>();
Channel channel = this;
while (channel != null)
{
result.Add(channel);
channel = myChannels.Where(c => c.ParentChannelId == this.Id).First();
}
result.Reverse();
return result;
}
}
EDIT: guess i did not explain myself as i should...
i have all the properties like ancestors, children parent etc...
i want to select all the classes that might contain the specific class...
I've re-read your question, especially the last part where you said you already have .Ancestors, and now it makes more sense.
Do this to get your list of items to delete:
List<MyClass> itemsToDelete = myItems
.Where(i => i.Id == myItemId)
.SelectMany(i => i.Ancestors)
.Concat(myItems) // Want to delete these too, not just the ancestors
.ToList()
;
Then you can foreach through the result, and remove them from the original list.
I'd suggest keeping these in a Dictionary<int, MyClass> or a HashSet<MyClass> instead of a list, since removal will be way faster.
For a HashSet, you'll have to implement Equals and GetHashCode, or create an IEqualityComparer<MyClass> implementation to provide those methods.
Before Edit:
I wouldn't write my code this way. I'd simply create a Dictionary<int, MyClass> instead of a list. It will do a lookup way faster than anything involving ancestors/tree traversal.
But here is how to accomplish what you're trying to accomplish:
If you're using Linq to Objects (as opposed to Linq to SQL or Linq to Entities), make a property called Parent on MyClass, of the correct type, instead of trying to link them by Id.
Then you can make an Ancestors property fairly easily:
public IEnumerable<MyClass> Ancestors
{
get
{
MyClass current = this;
while(current != null)
{
current = current.Parent;
yield return current;
}
}
}
If you can't edit the class, make an extension method called GetAncestors.
Then you can use something very similar to the code you wrote in your question:
List<MyClass> itemsToDelete = myItems
.Where(i => i.Ancestors.Any(a => a.Id == myItemId))
.ToList();
Linq to Entities
If you are using Linq to Entities, create a navigation property of the type MyClass to navigate to the parent, and do the same thing. Note that this might cause re-queries. Not sure the Linq can or would get translated into a hierarchical query.
This is how I would do it using a hashset and RemoveAll method.
var itemsToDelete = new HashSet<myClass>(otherItems);
myItems.RemoveAll(i => itemsToDelete.Contains(i));
RemoveAll Method
http://msdn.microsoft.com/en-us/library/wdka673a.aspx

list.ToArray vs list

I noticed something strange and there is a possibility I am wrong.
I have an interface IA and class A:
interface IA { .... }
class A : IA { .... }
In other class I have this:
private IList<A> AList;
public IList<IA> {
get { return AList; }
}
But I get compilation error.
But if I change it to:
public IList<IA> {
get { return AList.ToArray(); }
}
Everything is fine.
Why is it?
Why this doesn't work
private IList<A> AList;
public IList<IA> { get { return AList; } }
Exposing the property as IList<IA> would allow you to try to add class B : IA to the list, but the underlying list is really IList<A>, B is not A, so this would blow up in your face. Thus, it is not allowed.
Why this works:
public IList<IA> { get { return AList.ToArray(); } }
Array variance is broken. You can return the list as an array, it will still blow up in your face at runtime if you tried an Add operation (or try to replace an object at a given index with something other than an object of type A, but it's legal at compile time. A different example of this variance at play:
string[] array = new string[10];
object[] objs = array; // legal
objs[0] = new Foo(); // will bite you at runtime
From comments:
So what you suggest to use? How can I make the property return valid
object? How can I make the return value read only?
If consumers only need to iterate over the sequence and not have random, indexed access to it, you can expose the property as an IEnumerable<IA>.
public IEnumerable<IA> TheList
{
get { return AList.Select(a => a); }
}
(The Select is actually not technically needed, but using this will prevent consumers from being able to cast the result to its true underlying List<> type.) If the consumers decide they want a list or an array, they are free to call ToList() or ToArray() on it, and whatever they do with it (in terms of adding, removing, replacing items) will not affect your list. (Changes to the items' properties would be visible.) Similarly, you could also expose the collection an IList<IA> yourself in a safe way
public IList<IA> TheList
{
get { return AList.ToList<IA>(); }
}
Again, this would return a copy of the list, so any changes to it would not affect your underlying list.
Because native arrays are broken. This code is bad, you shouldn't do it, and the C# designers wish desperately they could undo it.
Arrays are covariant but lists are not.

An item in IEnumerable does not equal an item in List

I just can't figure out why the item in my filtered list is not found. I have simplified the example to show it. I have a class Item...
public class Item
{
public Item(string name)
{
Name = name;
}
public string Name
{
get; set;
}
public override string ToString()
{
return Name;
}
}
... and a class 'Items' which should filter the items and check if the first item is still in the list...
public class Items
{
private IEnumerable<Item> _items;
public Items(IEnumerable<Item> items)
{
_items = items;
}
public List<Item> Filter(string word)
{
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
Console.WriteLine("found: " + ret.Contains(_items.First()));
// found: false
return ret;
}
}
The executing code looks like this:
static void Main(string[] args)
{
string[] itemNames = new string[] { "a", "b", "c" };
Items list = new Items(itemNames.Select(x => new Item(x)));
list.Filter("a");
Console.ReadLine();
}
Now, if I execute the program, the Console.WriteLine outputs that the item is not found. But why?
If I change the first line in the constructor to
_items = items.ToList()
then, it can find it. If I undo that line and call ToList() later in the Filter-method, it also cannot find the item?!
public class Items
{
private IEnumerable<Item> _items;
public Items(IEnumerable<Item> items)
{
_items = items;
}
public List<Item> FilteredItems
{
get; set;
}
public List<Item> Filter(string word)
{
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
_items = _items.ToList();
Console.WriteLine("found: " + ret.Contains(_items.First()));
// found: false
return ret;
}
}
Why is there a difference where and when the lambda expression is executed and why isn't the item found any more? I don't get it!
The reason is deferred execution.
You intialize the _items field to
itemNames.Select(x => new Item(x));
This is a query, not the answer to that query. This query is executed every time you iterate over _items.
So in this line of your Filter method:
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
the source array is enumerated and a new Item(x) created for each string. These items are stored in your list ret.
When you call Contains(_items.First()) after that, First() again executes the query in _items, creating new Item instances for each source string.
Since Item's Equals method is probably not overridden and performs a simple reference equality check, the first Item returned from the second iteration is a different instance of Item than the one in your list.
Let's remove extra code to see the problem:
var itemNames = new [] { "a", "b", "c" };
var items1 = itemNames.Select(x => new Item(x));
var surprise = items1.Contains(items1.First()); // False
The collection items1 appears not to contain its initial element! (demo)
Adding ToList() fixes the problem:
var items2 = itemNames.Select(x => new Item(x)).ToList();
var noSurprise = items2.Contains(items2.First()); // True
The reason why you see different results with and without ToList() is that (1) items1 is evaluated lazily, and (2) your Item class does not implement Equals/GetHashCode. Using ToList() makes default equality work; implementing custom equality check would fix the problem for multiple enumeration.
The main lesson from this exercise is that storing IEnumerable<T> that is passed to your constructor is dangerous. This is only one of the reasons; other reasons include multiple enumeration and possible modification of the sequence after your code has validated its input. You should call ToList or ToArray on sequences passed into constructors to avoid these problems:
public Items(IEnumerable<Item> items) {
_items = items.ToList();
}
There are two problems in your code.
First problem is that you are initializing a new item every time. That is you don't store the actual items here when you write.
IEnumerable<Item> items = itemNames.Select(x => new Item(x));
The execution of Select is deferred. i.e every time you call .ToList() a new set of Items is created using itemNames as source.
Second problem is that you are comparing items by reference here.
Console.WriteLine("found: " + ret.Contains(_items.First()));
When you use ToList you store items in list and the references remains same so you will find item with reference.
When you don't use ToList the references are not same any more. because everytime a new Item is created. you cant find your item with different reference.

Categories

Resources