如何在 C# 中解决 "InvalidCastException"?

How do I solve an "InvalidCastException" in C#?

我收到一个 运行-time 错误,它告诉我无法将 PictureBox 类型的对象转换为类型 MusicNote(MusicNote 继承自 PictureBox。)

private void Note_MouseDown(object sender, MouseEventArgs e)
    {
        //try
        //{
            foreach (MusicNote mn in panel2.Controls) //this is where the error occurs
            {
                if (sender == mn)
                {
                    if (e.Button == MouseButtons.Right)
                    {
                        count = 0;
                        timer1.Start();
                        sp.SoundLocation = MusicNote_path + mn.note + ".wav";
                        sp.Play();
                    }
                if (e.Button == MouseButtons.Left)
                {
                    dragging = true;
                    mn.BackColor = Color.HotPink;


                }

下面是 MusicNote class 的一部分,包括构造函数,以显示每次构造 MusicNote 时发生的情况:

class MusicNote : PictureBox
{
    public int notepitch;
    public int noteduration;
    public String noteshape;
    public String note;
    enum Accid { sharp, flat, sole };

    public static String NoteImage_path = Environment.CurrentDirectory + @"\Notes-Images\";
    public static int xLoc = 30;
    public int yLoc = 0;

    System.Timers.Timer timer1 = new System.Timers.Timer();

    public MusicNote(int iNotepitch, int iDuration, String iBnoteShape, String iNote)
    {
        notepitch = iNotepitch;
        noteduration = iDuration;
        noteshape = iBnoteShape;
        note = iNote;


        ImageLocation =  NoteImage_path + noteshape + ".bmp";
        BackColor = Color.Transparent;
        ClientSize = new Size(35, 35);
        //BringToFront();


        Location = new Point(xLoc, getyLoc(iNote));
        xLoc += 37;
    }

面板的填充方式如下:

MusicNote mn = new MusicNote(mk.getMusicNote(), duration, bNoteShape, mk.getNote());
mn.MouseDown += new MouseEventHandler(Note_MouseDown);
mn.MouseUp += new MouseEventHandler(Note_MouseUp);
mn.MouseClick += new MouseEventHandler(Note_MouseClick);

panel2.Controls.Add(mn); //adding MusicNote component to MusicStaff (panel2) collection

编辑:可以查看错误here。

感谢任何帮助,谢谢。

panel2.Controls 不仅包含 MusicNotePictureBox 类型的 Controls 时,会发生此错误。根据您的数据,不清楚所有 panel2.Controls 的类型是什么,应该只是 PictureBoxes 的 CollectionList。如果 panel2.Controls 包含不同于 PictureboxMusicNote 类型的任何 Control(例如 TextBox 等),您将收到错误消息。如果所有 panel2.Controls 的类型都是正确的,那么很可能 panel2 在错误发生时没有完全加载。 你可以试试:

foreach (var mn in panel2.Controls)
if (sender == (MusicNote)mn)

要仅循环 MusicNote 个实例,您可以使用 LINQ 中的 OfType 扩展方法:

foreach (MusicNote mn in panel2.Controls.OfType<MusicNote>()) {
   // do stuff
} 

每次 foreach (MusicNote mn in panel2.Controls) 循环发现 MusicNote.

之后的任何其他内容时,都会发生此错误

您可以通过循环所有 Controls 来避免这种情况,例如 foreach (Control cntrl in panel2.Controls)

示例代码:

foreach (Control cntrl in panel2.Controls) 
            {
                if(cntrl is MusicNote)
                {
                     //do something with the MusicNote Control
                }
            }

当您的程序在 Note_MouseDown 等事件处理程序中获得控制权时,您会收到对 'took' UI (object sender) 事件的控件的引用。

尝试使用 as 子句将 sender 转换为 MusicNote。如果无法转换(因为 sender 不是 MusicNote),使用 as 子句不会抛出异常——相反它只会给你一个 NULL 引用,它你可以测试。

尝试这样的事情:

private void Note_MouseDown(object sender, MouseEventArgs e)
{
    var mn = sender as MusicNote;
    if (mn != null) 
    {
        if (e.Button == MouseButtons.Right)
        {
            count = 0;
            mn.timer1.Start();
            sp.SoundLocation = MusicNote_path + mn.note + ".wav";
            sp.Play();
        }
        if (e.Button == MouseButtons.Left)
        {
            dragging = true;
            mn.BackColor = Color.HotPink;
        }
    }
}

你真的不需要foreach

有很多方法可以解决您的问题,但让我们先浏览一下您的代码,以便您了解它有多么笨拙。用简单的英语来说,你正在这样做:

The mouse button is pressed down on a control. Which control? Well the control in sender. You loop through all the controls in panel2 to see if one of them is the sender and then you do some work.

但是为什么要遍历 panel2 中的所有控件?当您创建 MusicNote 控件时,您专门为该控件创建了这个事件处理程序,以便在鼠标按下时通知您。现在控件正在引发一个事件并说 "Hey, the mouse button is down and it is down on me!" 你看,即使 panel2.Controls.OfType<MusicNote>() 会解决你的问题,但你为什么要这样做?好像是 XY problem.

你会做你正在做的事情的唯一原因是,如果你创建了控件,订阅了 MouseDown 事件,然后以编程方式将控件从一个面板移动到另一个面板,而你只想做一些工作如果当鼠标按下时控件恰好位于 panel2 中。我怀疑你把它搬来搬去;即使你这样做了,也有更好的方法来处理这种情况。

正解

你不需要循环,你只需要这个:

private void Note_MouseDown(object sender, MouseEventArgs e)
{
    // If neither right nor left is down, return immediately because nothing needs 
    // to be done.
    if (!(e.Button == MouseButtons.Right || e.Button == MouseButtons.Left))
    {
        return;
    }

    // This should not fail, if it does, then ask yourself why have you created 
    // this handler for things which are not MusicNote.
    MusicNote mn = (MusicNote)sender;

    // Do some work below
    if (e.Button == MouseButtons.Right)
    {
        count = 0;
        timer1.Start();
        sp.SoundLocation = MusicNote_path + mn.note + ".wav";
        sp.Play();
    }

    if (e.Button == MouseButtons.Left)
    {
        dragging = true;
        mn.BackColor = Color.HotPink;
    }
}