C#运行时错误:带有通用类实例的InvalidCastException

时间:2015-12-01 07:44:28

标签: c# generics casting runtime-error typechecking

我是一名java开发人员,是C#的新手,我在下面的代码中遇到了 InvalidCastException

我已经基于自定义双向链接列表实现实现了一个队列。这两种实现都是通用的,因此,在测试代码中,我想使用通用的打印方法。

下面的测试代码工作正常;

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);
            }
        }
    }
}

但是,我想让 PrintQueue 静态方法适用于所有类型,因此我更改了方法代码,如下所示;

public static void PrintQueue(object queue)
{
    Queue<object> q = (Queue<object>)queue;

    foreach (object i in q)
    {
        Console.WriteLine(i);
    }
}

不起作用的整个测试代码如下;

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);
            }
        }
    }
}

然后我坚持使用 InvalidCastException 。问题出在哪里?如何修复此异常以及使此代码具有通用性的最佳做法是什么?

在java上,Object是每个类实例的基类,根类。因此,我已经将堆栈实例作为对象传递给该方法,假设它不会成为问题因为int,Int32的别名也从Object扩展。

下面,我将添加我用于编译上述测试代码的所有其余文件;

1)这是我在Doubly Linked List实现中使用的 DoublyLinkedListNode 类;

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)这是我的DoublyLinkedList类,它实现了我将要使用的队列实现的双向链表;

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)这是我基于上面使用的双链表实现的队列实现;

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();
        }
    }
}

3 个答案:

答案 0 :(得分:4)

从根本上说,您不应该尝试让PrintQueue每个对象工作 - 您应该尝试让它适用于任何Queue<T> ...并且最简单的方法是使其通用:

public static void PrintQueue<T>(Queue<T> queue)
{
    foreach (T item in queue)
    {
        Console.WriteLine(item);
    }
}

或者你可以更一般并接受IEnumerable<T>

public static void PrintSequence<T>(IEnumerable<T> queue)
{
    foreach (T item in queue)
    {
        Console.WriteLine(item);
    }
}

您的原始代码失败了,因为Queue<int>不是Queue<object> ...事实上,因为Queue<T>T中不是协变的,您无法将Queue<string>转换为Queue<object> ...但IEnumerable<T> 协变,所以:

Queue<string> stringQueue = new Queue<string>();
...
PrintSequence<object>(stringQueue);

... 没问题。

答案 1 :(得分:1)

如何更改PrintQueue方法。就像这样:

    public static void PrintQueue<T>(object queue)
    {
        var q = (Queue<T>)queue;

        foreach (var i in q)
            Console.WriteLine(i);
    }

并像这样使用它:

PrintQueue<int>(queue);

答案 2 :(得分:1)

像这样更改你的代码:

public static void PrintQueue(dynamic queue)
{
    foreach (var i in queue)
    {
        Console.WriteLine(i);
    }
}