I'm a java developer and new to C#, I'm stuck with InvalidCastException on the following code below.
I have implemented a Queue, based on a custom Doubly Linked List implementation. Both implementations are generic, and thus, on the test code, I want to use a generic print method.
The test code below is just working fine;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class TestQueue
{
public static void Main(string[] args)
{
Queue<int> queue = new Queue<int>();
queue.Enqueue(4);
queue.Enqueue(5);
queue.Enqueue(6);
Console.WriteLine("*****");
PrintQueue(queue);
Console.WriteLine("*****");
int first = queue.Dequeue();
Console.WriteLine("Enqueued value : " + first);
Console.WriteLine("*****");
PrintQueue(queue);
}
public static void PrintQueue(object queue)
{
Queue<int> q = (Queue<int>)queue;
foreach (object i in q)
{
Console.WriteLine(i);
}
}
}
}
However, I want to make the PrintQueue static method working for all types, thus I've changed the method code as below;
public static void PrintQueue(object queue)
{
Queue<object> q = (Queue<object>)queue;
foreach (object i in q)
{
Console.WriteLine(i);
}
}
The whole test code which is not working is as below;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class TestQueue
{
public static void Main(string[] args)
{
Queue<int> queue = new Queue<int>();
queue.Enqueue(4);
queue.Enqueue(5);
queue.Enqueue(6);
Console.WriteLine("*****");
PrintQueue(queue);
Console.WriteLine("*****");
int first = queue.Dequeue();
Console.WriteLine("Enqueued value : " + first);
Console.WriteLine("*****");
PrintQueue(queue);
}
public static void PrintQueue(object queue)
{
Queue<object> q = (Queue<object>)queue;
foreach (object i in q)
{
Console.WriteLine(i);
}
}
}
}
And then I'm stuck with the InvalidCastException. Where is the problem and how can I fix this exception and what is the best practice to make this code generic?
On java, Object is the base, root class of every class instance. Thus, I've passed the stack instance as an object to the method, assuming that it won't be a problem because int, the alias for Int32 also extended from the Object.
Here down below, I am adding the whole rest files that I've used for compiling the test code above;
1) Here is the DoublyLinkedListNode class that I used in Doubly Linked List implemetation;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class DoublyLinkedListNode<T>
{
// Fields
public T Value { get; set; }
public DoublyLinkedListNode<T> Previous { get; set; }
public DoublyLinkedListNode<T> Next { get; set; }
public DoublyLinkedListNode(T value)
{
Value = value;
}
}
}
2) Here is my DoublyLinkedList class which implements a doubly linked list for the queue implementation I am going to use;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class DoublyLinkedList<T> : ICollection<T>
{
#region Fields
public DoublyLinkedListNode<T> Head { get; private set; }
public DoublyLinkedListNode<T> Tail { get; private set; }
#endregion
#region Constructor
#endregion
#region Add
public void AddFirst(T value)
{
AddFirst(new DoublyLinkedListNode<T>(value));
}
public void AddFirst(DoublyLinkedListNode<T> node)
{
DoublyLinkedListNode<T> temp = Head;
Head = node;
Head.Next = temp;
//if(Count == 0)
if (Empty)
{
Tail = Head;
}
else
{
temp.Previous = Head;
}
Count++;
}
public void AddLast(T value)
{
AddLast(new DoublyLinkedListNode<T>(value));
}
public void AddLast(DoublyLinkedListNode<T> node)
{
//if (Count == 0)
if (Empty)
{
Head = node;
}
else
{
Tail.Next = node;
node.Previous = Tail;
}
Tail = node;
Count++;
}
#endregion
#region Remove
public void RemoveFirst()
{
//if (Count != 0)
if (!Empty)
{
Head = Head.Next;
Count--;
if (Count == 0)
{
Tail = null;
}
else
{
Head.Previous = null;
}
}
}
public void RemoveLast()
{
//if (Count != 0)
if (!Empty)
{
if (Count == 1)
{
Head = null;
Tail = null;
}
else
{
Tail.Previous.Next = null;
Tail = Tail.Previous;
}
Count--;
}
}
#endregion
#region ICollection
public int Count
{
get;
private set;
}
public void Add(T item)
{
AddFirst(item);
}
public bool Contains(T item)
{
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
if (current.Value.Equals(item))
{
return true;
}
current = current.Next;
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
array[arrayIndex++] = current.Value;
current = current.Next;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public bool Remove(T item)
{
DoublyLinkedListNode<T> previous = null;
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
if (current.Value.Equals(item))
{
if (previous != null)
{
previous.Next = current.Next;
if (current.Next == null)
{
Tail = previous;
}
else
{
current.Next.Previous = previous;
}
Count--;
}
else
{
RemoveFirst();
}
return true;
}
previous = current;
current = current.Next;
}
return false;
}
public void Clear()
{
Head = null;
Tail = null;
Count = 0;
}
//System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
public IEnumerator<T> GetEnumerator()
{
DoublyLinkedListNode<T> current = Head;
while (current != null)
{
yield return current.Value;
current = current.Next;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
//return ((System.Collections.Generic.IEnumerable<T>)this).GetEnumerator();
return this.GetEnumerator();
}
#endregion
#region helper
public void PrintEnumerable()
{
IEnumerator<T> e = this.GetEnumerator();
while (e.MoveNext())
{
T val = e.Current;
Console.WriteLine("Value: " + val);
}
}
public void PrintReverse()
{
for (DoublyLinkedListNode<T> node = Tail; node != null; node = node.Previous)
{
Console.WriteLine(node.Value);
}
}
public bool isEmpty()
{
if (Count == 0)
{
return true;
}
return false;
}
public bool Empty { get { return isEmpty(); } }
public DoublyLinkedListNode<T> First { get { return this.Head; } }
public DoublyLinkedListNode<T> Last { get { return this.Tail; } }
#endregion
}
}
3) And this is my Queue implementation based on doubly linked list implemetation that I've used above;
using System;
using System.Collections.Generic;
namespace Queue01
{
public class Queue<T> : System.Collections.Generic.IEnumerable<T>
{
DoublyLinkedList<T> _items = new DoublyLinkedList<T>();
LinkedList<T> hede = new LinkedList<T>();
public void Enqueue(T item)
{
_items.AddLast(item);
}
public T Dequeue()
{
if(_items.Count == 0)
{
throw new InvalidOperationException("The queue is empty.");
}
T value = _items.First.Value;
_items.RemoveFirst();
return value;
}
public IEnumerator<T> GetEnumerator()
{
return this._items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
Fundamentally, you shouldn't try to make PrintQueue
work for every object - you should try to make it work for any Queue<T>
... and the simplest way of doing that is to make it generic:
public static void PrintQueue<T>(Queue<T> queue)
{
foreach (T item in queue)
{
Console.WriteLine(item);
}
}
Or you could be more general and accept an IEnumerable<T>
:
public static void PrintSequence<T>(IEnumerable<T> queue)
{
foreach (T item in queue)
{
Console.WriteLine(item);
}
}
Your original code is failing because a Queue<int>
isn't a Queue<object>
... in fact, because Queue<T>
isn't covariant in T
, you can't convert Queue<string>
to Queue<object>
... but IEnumerable<T>
is covariant, so:
Queue<string> stringQueue = new Queue<string>();
...
PrintSequence<object>(stringQueue);
... would be okay.
See more on this question at Stackoverflow