I have implemented a generic custom collection class which only takes a Person type object.
While providing support for the Enumerator to iterate through the collection it shows an error
Cannot apply indexing with [] to an expression of type 'CustomCollection.CustomCollection'
Below is the code snippet for the class I created.
public class CustomCollection<T> : ICollection<T> where T : Person
{
private IList<T> lst = null;
public CustomCollection()
{
lst = new List<T>();
}
public void Add(T item)
{
this.lst.Add(item);
}
public void Clear()
{
this.lst.Clear();
}
public bool Contains(T item)
{
bool result = false;
foreach (T obj in this.lst)
{
if (obj.Id.Equals(item.Id))
{
result = true;
break;
}
}
return result;
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { return this.lst.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
bool result = false;
foreach (T obj in this.lst)
{
if (obj.Id.Equals(item.Id))
{
this.lst.Remove(item);
}
}
return result;
}
public IEnumerator<T> GetEnumerator()
{
return new PersonEnumerator<T>(this);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
The Enumerator class as below:
public class PersonEnumerator<T> : IEnumerator<T> where T : Person
{
private CustomCollection<T> collection = null;
private int index;
private T current;
public PersonEnumerator(CustomCollection<T> collection)
{
this.collection = collection;
this.index = -1;
current = default(T);
}
public T Current
{
get { return this.current; }
}
public void Dispose()
{
throw new NotImplementedException();
}
object IEnumerator.Current
{
get { return this.Current; }
}
public bool MoveNext()
{
if (++this.index >= collection.Count)
{
return false;
}
else
{
this.current = collection[this.index];
}
return true;
}
public void Reset()
{
this.index = -1;
current = default(T);
}
}
Person Class only contains FirstName, LastName & Id as properties in it.
ICollection doesn't implements an indexer. If you want to use indexing, you should implement IList instead.
You can use Linq with ICollection:
var result = Items.ElementAt(index);
Or you can add your own indexer:
public T this[int i]
{
return Items[i];
}
To make your code build, you need to add an indexer, to your CustomCollection<T> class, e.g.
public T this[int index]
{
return lst [index];
}
Then you can say use collection[n] to get the nth element in the MoveNext() method.
Or
Instead of having your PersonEnumerator<T> class you could just expose lst.GetEnumerator() which returns an IEnumerator<T>, i.e.
public IEnumerator<T> GetEnumerator()
{
return lst.GetEnumerator();
}
Unless you're planning on doing anything fancy with your enumerator, you might as well use the one for lst.
Related
I red a few articles on internet but all value to me, I couldn't understand how can I avoid adding a duplicate object to a list, I tried something like this.
I actually have created a class which overrides GetHashCode and Equal method.
Now I want to form a collection of non duplicate object list.
public class FlightInfo
{
public string Origin { get; set; }
public string DepartureTime { get; set; }
public string Destination { get; set; }
public string DestinationTime { get; set; }
public string Price { get; set; }
public override bool Equals(object obj)
{
var other = obj as FlightInfo;
if (other == null)
return false;
if (Origin != other.Origin || DepartureTime != other.DepartureTime || Destination != other.Destination
|| DestinationTime != other.DestinationTime || Price != other.Price)
return false;
return true;
}
public override int GetHashCode()
{
int hashOrigin = Origin.GetHashCode();
int hashDestination = Destination.GetHashCode();
int hashDepartureTime = DepartureTime.GetHashCode();
int hashDestinationTime = DestinationTime.GetHashCode();
int hashPrice = Price.GetHashCode();
return hashOrigin ^ hashDestination ^ hashDepartureTime ^ hashDestinationTime ^ hashPrice;
}
}
I also tried one article by Eric
https://blogs.msdn.microsoft.com/ericlippert/2011/02/28/guidelines-and-rules-for-gethashcode/
but this article has
private List<T>[] buckets = new List<T>[100];
insead of private List<T>() buckets = new List<T>()
but I want to return a list with no fix size.
Since you already implemented the Equals and GetHashCode methods you can have your own custom list of FlightInfo that will make use of those methods:
public class FlightInfoList : IList<FlightInfo>
{
private readonly List<FlightInfo> _flightInfos = new List<FlightInfo>();
public IEnumerator<FlightInfo> GetEnumerator()
{
return _flightInfos.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(FlightInfo item)
{
if (_flightInfos.Any(flightInfo => flightInfo.Equals(item)))
{
throw new Exception("Cannot add duplicated values!");
}
_flightInfos.Add(item);
}
public void Clear()
{
_flightInfos.Clear();
}
public bool Contains(FlightInfo item)
{
return _flightInfos.Contains(item);
}
public void CopyTo(FlightInfo[] array, int arrayIndex)
{
_flightInfos.CopyTo(array, arrayIndex);
}
public bool Remove(FlightInfo item)
{
return _flightInfos.Remove(item);
}
public int Count => _flightInfos.Count;
public bool IsReadOnly => false;
public int IndexOf(FlightInfo item)
{
return _flightInfos.IndexOf(item);
}
public void Insert(int index, FlightInfo item)
{
_flightInfos.Insert(index, item);
}
public void RemoveAt(int index)
{
_flightInfos.RemoveAt(index);
}
public FlightInfo this[int index]
{
get => _flightInfos[index];
set => _flightInfos[index] = value;
}
}
Notice that in the Add method I'm checking if there's a duplicated. Another way to solve this is to use a dictionary.
I have a custom queue class that is implemented via linked list, but I can't figure out how ot implement IEnumerable for something that is not array.
It's easy to implement it using Dequeue(), but I don't want enumeration to mutate my collection.
Here is my code for DQueue class:
class DQueue<Item> : IEnumerable<Item>
{
private Node<Item> startNode;
private Node<Item> lastNode;
private int _size;
public DQueue()
{
_size = 0;
}
public void Enqueue(Item item)
{
_size++;
if (startNode == null) {
startNode = new Node<Item>();
startNode.data = item;
lastNode = startNode;
} else {
Node<Item> temp = new Node<Item>();
temp.data = item;
lastNode.next = temp;
lastNode = temp;
}
}
public Item Dequeue()
{
Item temp = startNode.data;
startNode = startNode.next;
_size--;
return temp;
}
public bool IsEmpty()
{
return startNode == null;
}
public int Size()
{
return _size;
}
private class Node<InnerItem>
{
public InnerItem data;
public Node<InnerItem> next;
public Node()
{
next = null;
}
}
// IEnumerable
public IEnumerator<Item> GetEnumerator()
{
Node<Item> current;
for (int i = 0; i < _size; i++) {
//yield return values[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Suggest next solve, iteration from start node:
public IEnumerator<Item> GetEnumerator()
{
for (Node<Item> item = startNode; item != null; item = item.next)
{
yield return item.data;
}
}
public abstract class FieldObjects<T> : ConcurrentDictionary<int, T> where T : FieldObject
{
public Field Field { get; private set; }
public FieldObjects(Field field)
: base()
{
this.Field = field;
}
public virtual void Add(T value)
{
value.Field = this.Field;
value.ObjectId = this.Field.AssignObjectId();
this.AddOrUpdate(value.ObjectId, value, null);
}
public virtual void Remove(T value)
{
value.Field = null;
value.ObjectId = -1;
this.TryRemove(value.ObjectId, out value);
}
public IEnumerable<T> GetInRange(FieldObject reference, int range)
{
foreach (T fieldObject in this.Values)
{
if (reference.Position.DistanceFrom(fieldObject.Position) <= range)
{
yield return fieldObject;
}
}
}
}
I have this abstract class. I want to be able to iterate over it's values (not a KeyValuePair, but values only) in a safe way so I don't have to use the lock statement every-time. How can I do that?
as this link , I have public property although this error There was an error generating the XML document occur at ser.Serialize(sw, this); in SaveToXML method.
I have this class as parent, it has one property too!
public class XMLCollection<T> where T: new ()
{
private XmlSerializer ser;
private List<T> m_InnerList= new List<T>();
public List<T> InnerList {
get
{ return m_InnerList; }
set
{ m_InnerList = value; }
}
public XMLCollection()
{
ser = new XmlSerializer(this.GetType(), new XmlRootAttribute(this.GetType().Name ));
}
/// <summary>
///
/// </summary>
/// <param name="path">path format: #"D:\FolderName"</param>
public void SaveToXML(string path)
{
try
{
using (StreamWriter sw = new StreamWriter(path+ "\\"+this.GetType().Name+".xml"))
{
ser.Serialize(sw, this);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message );
}
}
public void LoadFromXML(string FullPath)
{
try
{
if (File.Exists(FullPath))
{
using (StreamReader sr= new StreamReader(FullPath))
{
this.InnerList=((XMLCollection<T>) ser.Deserialize(sr)).InnerList ;
}
}
else
{
Console.WriteLine("File not exist....");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message );
}
}
}
CollectionList is derived from it:
public class CollectionList :XMLCollection<Moshakhase>, IList<Moshakhase>, IDisposable
{
public enum SortType
{
Name, ID
}
#region ctor
public CollectionList()
{
}
public CollectionList(Moshakhase item)
{
this.Add(item);
}
public CollectionList(params Moshakhase[] item)
{
InnerList.AddRange(item);
}
#endregion
public override string ToString()
{
string str = this.GetType().Name + ":\r\n";
foreach (Moshakhase item in this)
{
str += string.Format("\t{0}({1})\r\n", item.Name, item.Id);
}
return str;
}
#region Sort
public void sort(SortType type)
{
switch (type)
{
case SortType.Name:
InnerList.Sort();
break;
case SortType.ID:
InnerList.Sort(new IDCompare());
break;
default:
break;
}
}
class IDCompare : IComparer<Moshakhase>
{
public int Compare(Moshakhase x, Moshakhase y)
{
if (x.Id > y.Id)
{
return 1;
}
else if (x.Id < y.Id)
{
return -1;
}
return 0;
}
}
#endregion
#region Ilist
public int IndexOf(Moshakhase item)
{
return InnerList.IndexOf(item);
}
public void Insert(int index, Moshakhase item)
{
InnerList.Insert(index, item);
}
public void RemoveAt(int index)
{
InnerList.RemoveAt(index);
}
public Moshakhase this[int index]
{
get
{
return InnerList[index];
}
set
{
InnerList[index] = value;
}
}
public void Add(Moshakhase item)
{
InnerList.Add(item);
}
public void Clear()
{
InnerList.Clear();
}
public bool Contains(Moshakhase item)
{
return InnerList.Contains(item);
}
public void CopyTo(Moshakhase[] array, int arrayIndex)
{
InnerList.CopyTo(array,arrayIndex );
}
public int Count
{
get { return InnerList.Count ; }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool Remove(Moshakhase item)
{
return InnerList.Remove(item);
}
public IEnumerator<Moshakhase> GetEnumerator()
{
return InnerList.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return InnerList.GetEnumerator();
}
#endregion
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
Users is derived from CollectionList :
public class Users : CollectionList
{
public Users()
{
}
public Users(ketabkhune.unique.User user): base(user)
{
}
public Users(params ketabkhune.unique.User[] user):base(user)
{
}
}
I use this code to check it:
Users uList = new Users(new User[] {new User(4,"Kamran"), new User(3,"Sara"), new User(5,"Bahar") });
uList.SaveToXML(#"F:\xml");
when I combined XMLCollection with CollectionList and define CollectionList as a generic class. that error disappeared..
Don't forget blank constructor
[Serializable()]
public class CollectionList<T> : IList<T>
{
private XmlSerializer ser;
private List<T> InnerList = new List<T>();
#region ctor
public CollectionList()
{
ser = new XmlSerializer(this.GetType());
}
public CollectionList(string CollectionName)
{
ser = new XmlSerializer(this.GetType(), new XmlRootAttribute(CollectionName ));
}
....
Change Your Code Where The Line Like Bellow :
ser.Serialize(sw, m_InnerList);
How to implement IEnumerator on this class so that I can use it in foreach loop.
public class Items
{
private Dictionary<string, Configuration> _items = new Dictionary<string, Configuration>();
public Configuration this[string element]
{
get
{
if (_items.ContainsKey(element))
{
return _items[element];
}
else
{
return null;
}
}
set
{
_items[element] = value;
}
}
}
In this example Configuration is a simple class with few properties.
Just an example to implement typesafe IEnumerable and not IEnumerator which you will be able to use in foreach loop.
public class Items : IEnumerable<Configuration>
{
private Dictionary<string, Configuration> _items = new Dictionary<string, Configuration>();
public void Add(string element, Configuration config) {
_items[element] = config;
}
public Configuration this[string element]
{
get
{
if (_items.ContainsKey(element))
{
return _items[element];
}
else
{
return null;
}
}
set
{
_items[element] = value;
}
}
public IEnumerator<Configuration> GetEnumerator()
{
return _items.Values.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _items.Values.GetEnumerator();
}
}
Regards.
You should be able to implement IEnumerator like this:
public class Items : IEnumerator<KeyValuePair<string, Configuration>>
{
private Dictionary<string, Configuration> _items = new Dictionary<string, Configuration>();
public Configuration this[string element]
{
get
{
if (_items.ContainsKey(element))
{
return _items[element];
}
else
{
return null;
}
}
set
{
_items[element] = value;
}
}
int current;
public object Current
{
get { return _items.ElementAt(current); }
}
public bool MoveNext()
{
if (_items.Count == 0 || _items.Count <= current)
{
return false;
}
return true;
}
public void Reset()
{
current = 0;
}
public IEnumerator GetEnumerator()
{
return _items.GetEnumerator();
}
KeyValuePair<string, Configuration> IEnumerator<KeyValuePair<string, Configuration>>.Current
{
get { return _items.ElementAt(current); }
}
public void Dispose()
{
//Dispose here
}
}
But as already noted you could also just implement IEnumerable.
You don't need to implement IEnumerable or any interface. In order to be able to use your class in a foreach, all that you need is to add an instance method to your class with the follwing signature:
IEnumerator GetEnumerator()