C# Custom Collection / Enumerator - c#

Given the following class:
public class GenClass<T>
{
private List<T> ItemsList {get;set;}
public Predicate<T> SomeCondition {get;set;}
public bool UsePredicate {get;set;}
public List<T> Items
{
get { //CODE Goes here; }
}
}
I need a way for the list to use the SomeConditionPredicate and return only the items than match the condition, but only if the bool UsePredicate is true. I know I can just use LINQ for this, the problem is that everytime I query with LINQ I get a different instance of an IEnumerable, and this needs to be a property, therefore I need to be able to access the same instance of the List from outside the class, because I will be adding and removing items from it, and I cannot do that with the result of a .Where, for example.
I was thinking of a custom IList<T>, but I'm not really sure how to do that.

You have a conceptual problem here. If it is the same instance, how is it supposed to filter by the condition? The reason why LINQ returns a new enumeration on every call is that it runs the query "live", and multiple queries have to be independent.
That said, you probably shouldn't have to rely on the property returning the same reference each time. If you rely on the instance being the same, what do you expect to happen when/if someone changes the predicate?
And how is adding or removing items supposed to work/act on a filtered list? If you add an item that would be filtered out, what happens?

Your question is a little unclear, but let's start with this:
public class GenClass<T>
{
private List<T> ItemsList {get;set;}
public Predicate<T> SomeCondition {get;set;}
public bool UsePredicate {get;set;}
public List<T> Items
{
get { return UsePredicate
? ItemsList.Where(SomeCondition).ToList()
: ItemsList; }
}
}
What about that doesn't work for your use case?

An IEnumerable can only be used to read the collection, but not to make changes to it. If you want to make changes to it, return an enumeration of filtered indexes instead.
public IEnumerable<int> FilteredIndexes
{
get
{
if (UsePredicate) {
return ItemsList
.Select((item, i) => i)
.Where(i => SomeCondition(ItemsList[i]));
}
return ItemsList.Select((item, i) => i);
}
}
Assuming that you have declared this indexer
public T this[int index]
{
get { return ItemsList[index]; }
set { ItemsList[index] = value; }
}
You can now use the collection like this
GenClass<string> stringCollection = new GenClass<string>();
//TODO: Add items
stringCollection.SomeCondition = s => s.StartsWith("A");
stringCollection.UsePredicate = true;
foreach (int index in stringCollection.FilteredIndexes) {
stringCollection[index] = stringCollection[index] + " starts with 'A'";
}
UPDATE
If you do not want to expose the indexes, you could create a class used as item accessor representing your collection items
public class Item<T>
{
private List<T> _items;
private int _index;
public Item(List<T> items, int index)
{
_items = items;
_index = index;
}
public T Value
{
get { return _items[_index]; }
set { _items[_index] = value; }
}
}
In your collection you would declare this property
public IEnumerable<Item<T>> FilteredItems
{
get
{
if (UsePredicate) {
return ItemsList
.Select((item, i) => new Item<T>(ItemsList, i))
.Where(item => SomeCondition(item.Value));
}
return ItemsList.Select((item, i) => new Item<T>(ItemsList, i));
}
}
Now you can use the collection like this
foreach (Item<string> item in stringCollection.FilteredItems) {
item.Value = item.Value + " starts with 'A'";
}
A general note: You can safely turn the private properties into fields. Properties are normally used as an intermediate to publicly expose field values.

Related

Better setters for lists of objects

Recently, when handling collections of objects of the same (base-)class,
I´ve recently found myself writing something like this:
class SomeClass {
public bool PropertyA {get; set;}
}
class EncapsulatingClass {
private List<SomeClass> list = new();
private bool propA;
public bool PropertyA {
get { return propA; }
set {
propA = value;
foreach(SomeClass instance in list)
instance.PropertyA = value;
}
}
}
This is of course so I don´t have to use foreach every time I want to set a property for the collection. While this works fine, I feel like this requires a lot of code for something simple and a lot of repitition with each property.
Is there a better solution, like extracting the logic of "apply this for the property of the same name for each object in the list" into a function and just calling that in the setters?
There is the issue of ownership of the property. If you need to enforce synchronization such that setting PropertyA ins the encapsulating class, all the instances in the list also use the same value.
For example
class SomeClass
{
public SomeClass(EncapsulatingClass parent)
{
Parent=parent;
}
public EncapsulatingClass Parent { get; }
public bool PropertyA { get => Parent.PropertyA; }
}
class EncapsulatingClass
{
private List<SomeClass> list = new List<SomeClass>();
private bool propA;
public bool PropertyA
{
get { return propA; }
set
{
propA = value;
}
}
}
Otherwise, you have multiple PropertyA values, one for each instance, and then you have to decide which one is the master value, and what to do if some are different.
I'm wondering what it is you are doing to need this so often. It makes me think there's a flaw in the design of your application you could avoid by restructuring something but it's difficult to say without more information.
For your specific problem I would discard EncapsulatingClass and use the ForEach method on List<T> for a little more concise code:
myList.ForEach(s => s.PropertyA = true);
Alternatively, if you don't always use List<T> you can write your own extension method to work on all IEnumerables:
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var t in source)
action(t);
}
// Call it just like previously:
myIEnumerable.ForEach(s => s.PropertyA = true);
Of course, this is still cumbersome if you need to do it a lot. But I suspect if you do, it's probably a flaw in the design.
I might approach this with a custom List class providing a single mass update method.
public class EasyUpdateList<T> : List<T>
{
public void UpdateAll(Action<T> update)
{
if (update == null)
return;
foreach (T item in this)
update(item);
}
}
Now you don't need a specific encapsulating class, you can just create a new EasyUpdateList and update any number of properties across the collection using the UpdateAll method.
EasyUpdateList<MyClass> list = new EasyUpdateList<MyClass>();
list.Add(instance1);
list.Add(instance2);
...
list.UpdateAll(x =>
{
x.Property1 = "Value1";
x.Property2 = "Value2";
});
This still uses a foreach loop but is much more generic and you don't have to change your other classes or write repeated code for each one.
Of course you could also achieve this with an extension method for a List class if you don't want a new class.
public static void UpdateAll<T>(this IList<T> list, Action<T> update)
{
if (update == null)
return;
foreach (T item in list)
update(item);
}

Select a Collection with same interface

If have following classen
public interface ISomething { int Id { get; set; } }
public class SomethingA : ISomething {...}
public class SomethingB : ISomething {...}
In another class I have following two lists:
List<SomethingA> aValues;
List<SomethingB> bValues;
My question is if there is a possibility to do something like this:
public List<ISomething> GetList(bool select) {
return select ? aValues : bValues;
}
My goal is to use this as this:
GetList(true).Single(x => x.Id) // or
foreach (var value in GetList(false))
{
value.Id = 18;
}
// anything else
UPDATE:
I see, there are good possibilities. But is there also a way to also achieve the following?
GetList(true).Remove(myValue);
You can't return List<ISomething> because List<T> is not covariant and classes can't be. IEnumerable<T> is covariant, you may use it as readonly sequence.
Change the method to return IEnumerable<ISomething>
public static IEnumerable<ISomething> GetList(bool select)
{
return select ? (IEnumerable<ISomething>)aValues :bValues;
}
Then do
var result = GetList(true).Single(x => x.Id == 0);
foreach (var value in GetList(false))
{
value.Id = 18;
}
As for your update: If you like to remove the item you need to lose some flexibility. I.e Use non generic IList as the return type.
public static IList GetList(bool select)
{
return select ? (IList)aValues : bValues;
}
Then do
IList list = GetList(true);
foreach (var value in list.OfType<ISomething>())//OfType or Cast can be used
{
if (value.Id == 6)//Whatever condition
{
list.Remove(value);
break;
}
}
I like the OfType extension because it returns the typed list you need
var listA = initialList.OfType<TypeA>(); //return List<TypeA>
var listB = initialList.OfType<TypeB>(); //return List<TypeB>
So in your case you start with
var aValues = List<ISomething>.OfType<SomethingA>()
and then you can iterate on whichever subcollection you need. Of course you are then working with a IEnumerable, but that can be converted implicitly back to a IEnumerable<ITest>.
If you want to filter out values, I would create explicit methods to remove them but it depends on what you need to achieve in the end (for example comparing on a Id instead of the whole object):
public IEnumerable<T> Remove<T>(this List<IDisposable> values, T valueToRemove) where T: IComparable
{
return values.OfType<T>().Where(t => valueToRemove.CompareTo(t) != 0);
}
The simplest solution may be using Linq Cast() like this:
public List<ISomething> GetList(bool select)
{
return (List<ISomething>)(#select ? aValues.Cast<ISomething>() : bValues.Cast<ISomething>());
}
I see, there are good possibilities. But is there also a way to also achieve the following?
GetList(true).Remove(myValue);
To remove from the original lists, you are likely best of with a specialized Remove method on the class in question as others have suggested, as most solutions here return a copy of the original list.
You may remove the element from a copy of the list quite easily like so, but I understand that's not what you are asking.
var result = GetList(true);
result.Remove(myValue);
You can either use the .Cast<T> method like this:
if (select)
{
return aValues.Cast<ISomething>().ToList();
}
else
{
return bValues.Cast<ISomething>().ToList();
}
or add all items to a commong Lis() like this:
var ret = new List<ISomething>();
if (select)
{
ret.AddRange(aValues);
}
else
{
ret.AddRange(bValues);
}
return ret;
Since you only want to iterate it, I would write the method like this:
public IEnumerable<ISomething> GetList(bool select) {
return select ? aValues.Cast<ISomething>() : bValues.Cast<ISomething>();
}
You can also look at this StackOverflow question.

How to make such custom list/collection

I'm trying to develop a custom collection or list class which provides me the following capabilities:
Add(MyObject)
Add(MyObject, String) ' key
Remove(MyObject)
RemoveByKey(String) ' key
RemoveAt(Index)
Count
Clear
Item(Index)
Item(String) ' key
Contains(MyObject)
ContainsKey(String) ' key
GetEnumerator ' for-each MyObject
I've searched through IEnumerable, IList, ICollection but none are satisfying what I need above. For example, they're all missing storing of objects by Key(string).
How do I create such a collection/list object? I've noticed that the best thing that matches my requirements is the ListViewItemCollection object available by the system. I wish I could see the coding inside it to find out how it has implemented the storing and retrieval of objects.
Can anybody help out? Or guide me to tutorial links.
Thanks.
Example of such class could be System.Windows.Forms.Control.ControlCollection which is implemented like List<KeyValuePair<string,Control>> (actually Control already contains the key) and this[string] is implemented using ordinary for-loop (linear searching for the key).
We can help to speed this up by adding Dictionary and add every item with key to both collection (List+Dictionary). Items without key are added to List only.
EDIT: Further improvement may use List<KeyValuePair<string,T>> and Dictionary<string,KeyValuePair<int,T>> - mapping index from List to Dictionary for faster removing. RemoveAt should check if the key is preset and delete it from dictionary as well. RemoveByKey can get index for internal List.RemoveAt.
ADDON based on comments: implementation of IEnumerable<T> may look like this:
class MyObjectList<T>: IEnumerable<T> {
public IEnumerator<T> GetEnumerator() {
T[] a = items; int n = size;
for(int i = 0; i < n; i++)
yield return a[i]; }
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator(); }
...the above are internals of List<T>
ADDON: here you can see my custom ListCore and List created from it (feel free to use it as you wish).
I bet there are tons of easier ways to do this, but here's one approach. You could create a struct containing the key and value of each item:
public sealed class Listionary<K, T> : IDictionary<K, T>, IList<T>
{
private struct ListionaryPair
{
public ListionaryPair(T item) : this()
{
Item = item;
}
public ListionaryPair(K key, T item) : this()
{
Key = key;
Item = item;
}
public K Key { get; private set; }
public T Item { get; private set; }
public bool HasKey { get; private set; }
}
private readonly List<ListionaryPair> list = new List<ListionaryPair>();
(The whole HasKey thing allows value types as K, or null references as valid keys. If you only want string keys you could replace this struct with KeyValuePair<string, T>)
And then both interfaces separately:
public void Add(T item)
{
list.Add(new ListionaryPair(item));
}
public void Add(K key, T item)
{
list.Add(new ListionaryPair(key, item));
}
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
You can hide ugly methods by explicitly implementing them:
void ICollection<KeyValuePair<K, T>>.CopyTo(KeyValuePair<K, T>[] array, int arrayIndex)
{
// code implementing the method
}
You'll need some helper methods for access by key:
private int IndexOfKey(K key)
{
for (int i = 0; i < list.Count; i++)
{
var pair = list[i];
if (pair.HasKey && pair.Key == key)
{
return i;
}
}
return -1;
}
but if you get them right the rest won't be that much of a challenge:
public T this[K key]
{
get
{
int index = IndexOfKey(key);
if (index < 0)
{
throw new IndexOutOfRangeException();
}
return list[index].Item;
}
set
{
int index = IndexOfKey(key);
if (index < 0)
{
throw new IndexOutOfRangeException();
}
list[index] = new ListionaryPair(key, value);
}
}
It's quite a bit of coding to complete each interface method, but most will be short and simple. You'll have to decide whether you allow multiple items with the same key, whether IDictionary<,>.Clear() clears the entire collection or only keyed items, etc.
Also there's no backing Dictionary in this example, so performance might not be that great.

Property Accessors on List<T> in .NET

I have a property called Fruits which contains a comma delimited string in the form "apples,bananases,peaches"
I want to make a list in the same class which makes the Fruits property easier to manipulate. Accessors won't work as they are not supported on lists or so it seems.
Basically i want a property called FruitList which auto populates based on the Fruits Property and when adding items or manipulating the FruitList it should auto populate the fruits property.
I need the Fruits Property for Entity framework.
You can invert the logic:
List<string> fruitsList = new List<string>();
public List<string> FruitsList
{
get
{
return fruitsList;
}
}
public string Fruits
{
get
{
return string.Join(',', fruitsList);
}
set
{
// Incomplete, does not handle null
FruitsList.Clear();
FruitsList.AddRange(value.Split(','));
}
}
You don't need to worry about updating Fruits if Fruits is determined by looking at FruitsList. You mention that you need Fruits as a string property for Entity Framework, but EF does not care whether it is backed by a string field.
The only realistic way to do this is to use a collection which can be observed for changes, and handle the event raised when it is changed, and update the property.
Something like ObservableCollection<T> would fit the bill.
example:
public class MyObject
{
public string Fruits{get;set;}
public IList<string> FruitList
{
get
{
var list = new ObservableCollection<string>(Fruits.Split(','));
list.CollectionChanged += (s,ea) => {
var items = (IList<string>)s;
Fruits = String.Join(",",items);
};
return list;
}
}
}
Usage:
var obj= new MyObject(){ Fruits="Apple,Banana,Orange" };
var list = obj.FruitList;
list.Add("Satsuma");
list.Add("Grapes");
list.Remove("Apple");
Console.WriteLine(obj.Fruits); // Output: Banana,Orange,Satsuma,Grapes
Live example: http://rextester.com/KCT33825
Having seen the concept here work, it's worth noting that the above implementation is frought with a bit of danger. It creates a new ObservableCollection every time you call the get accessor on it, which could have some unintended consequences.
For example, if you add the following line just before my original Console.WriteLine:
Console.WriteLine("{0}", obj.FruitList == list);
It outputs false which might seem strange as you might (and notionally, should) expect list and obj.FruitList to point to the same list.
You can get round this by changing the implementation to create only ever 1 ObservableCollection and always returning that from the get accessor:
public class MyObject
{
private string fruits;
private ObservableCollection<string> fruitList;
public string Fruits
{
get{ return this.fruits; }
set
{
this.fruits = value;
this.fruitList = CreateFruitList();
}
}
private ObservableCollection<string> CreateFruitList()
{
var list = new ObservableCollection<string>(this.fruits.Split(','));
list.CollectionChanged += (s,ea) => {
var items = (IList<string>)s;
this.fruits = String.Join(",",items);
};
return list;
}
public IList<string> FruitList
{
get
{
return fruitList;
}
}
}
Now all is right with the world again!
here's what you could do, create a proxy for your comma separated list:
public class MyClass
{
public string Fruits {get;set;}
public string [] FruitList {
get { return Fruits.Split(new [] {','}); }
//warning, the setter is dangerous
set { Fruits = string.Join(',', value); }
}
}
When I say the setter is dangerous, I only mean that if you change one element of the array, the Fruit won't be updated. It'll only be updated if you push a new array. If you need that behavior, consider implementing it using an ObservableCollection

Generic type's causing issue C#.net

I have class called GroupItem, i can store any type here say int, string, decimal, datetime etc.., Then, i have GroupItems which will store any groupItem. I'm using an arraylist to store all the groupItem.
public class GroupItem<T>
{
private string heading;
private List<T> items = new List<T>();
public GroupItem() { }
public string Heading
{
get { return heading; }
set { heading = value; }
}
public List<T> Items
{
get { return items; }
set { items = value; }
}
public void Add(T value)
{
this.items.Add(value);
}
public T this[int index]
{
get
{
return this.items[index];
}
}
}
public class GroupItems
{
private string groupName;
private List<object> items = new List<object>();
public string GroupName
{
get { return groupName; }
set { groupName = value; }
}
public GroupItems() { }
public void Add(object value)
{
this.items.Add(value);
}
public object this[int index]
{
get
{
return this.items[index];
}
}
}
I want to retrieve from GroupItems. How i can get generic item's values in groupItems?
I'm now inserting two items, datetime and int to groupitems. Now i want to retrieve groupitems[2] value but how i can convert this to groupItem without knowing what it is. Even we may get its genericarguments by getType().getGenericarguments()[0]. But how i can create an instance based upon that.
If the list is storing heterogeneous items, then I would suggest you need a common non-generic interface or base-class. So, say we have
interface IGroupItem {
// the non-generic members, and maybe
// "object Value {get;}" etc, and maybe
// "Type ItemTypr {get;}"
}
You would then have:
class GroupItem<T> : IGroupItem {...}
an you would then use
List<IGroupItem> ...
instead of ArrayList, or, franky, in place of GroupItems {...}
What I'd do is create a generic collection such as:
public class GroupItems<T> : List<GroupItem<T>>
{
}
If you need to extend the basic functionality of a list, you could also extend Collection<T> and override the methods you need:
public class GroupItems<T> : Collection<GroupItem<T>>
{
protected override void InsertItem(int index, T item)
{
// your custom code here
// ...
// and the actual insertion
base.InsertItem(index, item);
}
}
How about just replacing your GroupItems class with List<GroupItem<T>> ?
Depending on what you do with GroupItem you should either inherit from List/Collection as was offered by other or use a generic collection inside your class
e.g.
class GroupItem<T>
{
private List<T> items = new List<T>();
public void Add(T value)
{
items.Add(value);
}
public T Get()
{
//replace with some logic to detemine what to get
return items.First();
}
}
There are two situations that could be covered by your question:
You want to simply store a collection of GroupItem's of type T in the class GroupItems.
You want to store a collection of generic GroupItem's of any type in the class GroupItems. To better clarify, I mean that you could store GroupItem<DateTime> or GroupItem<int> in the same GroupItems class.
Here are some ways of going about storing and retrieving for both scenarios:
Same Type
public class GroupItem<T>
{
// ... Code for GroupItem<T>
}
public class GroupItems<T>
{
private List<GroupItem<T>> mItems = new List<GroupItem<T>>();
public void Add(T item)
{
mItems.Add(item);
}
public T GetItem(int index)
{
return mItems[index];
}
}
Here you will build a collections that contain GroupItem's of the same time, so a collection of GroupItem<DateTime> for example. All the items will be of the same type.
Generic Type
public interface IGroupItem
{
// ... Common GroupItem properties and methods
}
public class GroupItem<T>
{
// ... Code for GroupItem<T>
}
public class GroupItems
{
private List<IGroupItem> mItems = new List<IGroupItem>();
public void Add(IGroupItem item)
{
mItems.Add(item);
}
// This is a generic method to retrieve just any group item.
public IGroupItem GetItem(int index)
{
return mItems[index];
}
// This is a method that will get a group item at the specified index
// and then cast it to the specific group item type container.
public GroupItem<T> GetItem<T>(int index)
{
return (GroupItem<T>)mItems[index];
}
}
Here you will be able to build and maintain a single collection that can contain any GroupItem with any Type. So you could have a GroupItems collection that contains items of GroupItem<DateTime>, GroupItem<int>, etc.
Please note that none of these code examples take into account any erroneous circumstances.
Consider: you have a collection of items; the items may have any runtime type (string, int, etc.). Because of this, the static type of the collections items must be object.
It seems that you want to be able to retrieve items from the list with strong static typing. That's not possible without a lot of conditional logic (or reflection). For example:
object item = collection[0];
if (item is int)
//do something with an int
else if (item is string)
//do something with a string
Now suppose instead of "doing something" with the value of collection[0], we assign the value to a variable. We can do one of two things:
use the same variable for both cases, in which case the static type must be object.
use separate variables, in which case the static type will be string or int, but outside of the conditional logic, we can't know which variable holds the value of collection[0].
Neither option really solves the problem.
By creating GroupItem<T>, you add a level of indirection to this problem, but the underlying problem is still there. As an exercise, try reworking the example, but starting from "Consider: you have a collection of items; the items are of type GroupItem<T> where T may be any runtime type (string, int, etc.)."
Thanks for your inputs.
I have resolved it myself using multiple overloading methods to resolve this.
for example:
private void Print(GroupItem<string> items)
{
///custom coding
}
private void Print(GroupItem<int> items)
{
///custom coding
}
Though its not efficient enough, i want to do in this way as it was .net 2.0.
I'm now improving this in .Net 4.0 with new algorithm.
Thanks a lot for all of your helps.

Categories

Resources