C# Runtime Error: InvalidCastException with Generic Class Instance

C# Runtime Error: InvalidCastException with Generic Class Instance

我是一名 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 上,对象是基础,每个 class 实例的根 class。因此,我已将堆栈实例作为对象传递给方法,假设这不会成为问题,因为 int,Int32 的别名也从 Object 扩展而来。

在下面,我添加了我用于编译上面测试代码的全部剩余文件;

1) 这是我在双向链表实现中使用的 DoublyLinkedListNode class;

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 class,它为我将要使用的队列实现实现了双向链表;

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

更改 PrintQueue 方法怎么样。就像这样:

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

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

并像这样使用它:

PrintQueue<int>(queue);

从根本上说,您不应该尝试让 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);

...没问题。

像这样更改您的代码:

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