【问题标题】:C# How do I set a column in a multidimensional array with a switch statement?C#如何使用switch语句在多维数组中设置列?
【发布时间】:2020-02-11 03:46:42
【问题描述】:

所以在这个类中,我尝试使用 switch 语句来确定在表单中选中了哪个单选按钮。

这是我关注的代码部分

private void lstTransactions_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = lstTransactions.SelectedIndex;
            if (index != -1)
            {
                txtAmount.Text = entries[index, 0];
                txtDate.Text = entries[index, 1];
                chkCleared.Checked = bool.Parse(entries[index, 3]);
                // entries[index, 2] is transaction type
                //make switch comparison work correctly
                // to determine which radio button should be checked

                switch(entries[index, 2])
                {
                    case TransactionTypes.Deposit.ToString():
                        rbDeposit.Checked = true;
                        break;
                    case TransactionTypes.Withdrawal.ToString():
                        rbWithdrawal.Checked = true;
                        break;
                    default:
                        rbServiceFee.Checked = true;
                        break;
                }
            }
        }

键入 case TransactionTypes.Deposit.ToString():case TransactionTypes.Withdrawal.ToString(): 给我一个错误,上面写着“CS0150 A constant value is expected”。我尝试查找错误,我仍然不知道如何进行开关比较。

这是完整的表单代码供参考

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


namespace Transaction3
{
    public partial class CheckbookForm : Form
    {
        public CheckbookForm()
        {
            InitializeComponent();
        }

        enum TransactionTypes { Deposit, ServiceFee, Withdrawal }
        TransactionTypes transactionType;

        private string[,] entries = new string[20, 4];



        decimal balance = 0m;
        decimal bankBalance = 0m;

        private void SetError(Control whichControl, string message)
        {
            errorProvider1.SetError(whichControl, message);
        }

        private decimal IsValid()
        {
            bool flag=true;
            decimal amount = 0m;

            DateTime date;
            if (DateTime.TryParse(txtDate.Text,out date))
            {
                if (date>DateTime.Today)
                {
                    flag=false;
                    SetError(txtDate,"Date must be on or before today");
                }
            }
            else
            {
                flag=false;
                SetError(txtDate,"Date must be entered");
            }

            if (decimal.TryParse(txtAmount.Text, out amount))
            {
                if (amount<=0)
                {
                    flag=false;
                    SetError(txtAmount,"Amount must be more than zero");
                }
            }
            else
            {
                flag = false;
                SetError(txtAmount,"Amount must be a number more than zero");
            }

            if (flag)
            {
                if (transactionType == TransactionTypes.Withdrawal)
                {
                    if (balance >= amount)
                    {
                        amount *= -1;
                    }
                    else
                    {
                        flag = false;
                        SetError(txtAmount, "Insufficient funds");
                    }
                }
                else if (transactionType == TransactionTypes.ServiceFee)
                    amount *= -1;
            }
            if (!flag)
                amount = 0;
            return amount;
        }

        private void ShowBalance()
        {
            lblBalance.Text = balance.ToString("c");
            lblBankBalance.Text = bankBalance.ToString("c");
        }

        private void ClearForm()
        {
            txtAmount.Clear();
            txtAmount.Focus();
            txtDate.Clear();
            rbWithdrawal.Checked = true;
        }

        private void CheckbookForm_Load(object sender, EventArgs e)
        {
            rbDeposit.Tag = TransactionTypes.Deposit;
            rbWithdrawal.Tag = TransactionTypes.Withdrawal;
            rbServiceFee.Tag = TransactionTypes.ServiceFee;
            ClearForm();
            ShowBalance();
        }

        private void btnProcess_Click(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            decimal amount = IsValid();
            if (amount != 0)
            {
                string entry;
                string process = "Not Processed";
                balance += amount;
                if (chkCleared.Checked)
                {
                    bankBalance += amount;
                    process = "Processed";
                }
                ShowBalance();
                int row = lstTransactions.Items.Count;
                entries[row, 0] = amount.ToString();
                entries[row, 1] = txtDate.Text;
                entries[row, 2] = transactionType.ToString();
                entries[row, 3] = chkCleared.Checked.ToString();

                entry = string.Format("{0} {1}: {2} ({3})", transactionType.ToString(),
                    txtDate.Text, amount.ToString("c"), process);
                lstTransactions.Items.Add(entry);
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            ClearForm();
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            DialogResult button;
            button = MessageBox.Show("Clear all prior entries and set balance to $0?\nThis CANNOT be undone.",
                "Reset Account", MessageBoxButtons.YesNo);
            if (button == System.Windows.Forms.DialogResult.Yes)            {

                balance = 0;
                bankBalance = 0;
                ClearForm();
                ShowBalance();
                lstTransactions.Items.Clear();
            }
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void rb_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = (RadioButton)sender;
            if (rb.Checked) 
                transactionType = (TransactionTypes) rb.Tag;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            btnExit_Click(sender, e);
        }

        private void CheckbookForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult button;
            button = MessageBox.Show("Close and exit? All entries will be lost.",
                "Exit?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question,
                 MessageBoxDefaultButton.Button2);
            if (button == System.Windows.Forms.DialogResult.Cancel)
                e.Cancel = true;
        }

        private void lstTransactions_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = lstTransactions.SelectedIndex;
            if (index != -1)
            {
                txtAmount.Text = entries[index, 0];
                txtDate.Text = entries[index, 1];
                chkCleared.Checked = bool.Parse(entries[index, 3]);
                // entries[index, 2] is transaction type
                //make switch comparison work correctly
                // to determine which radio button should be checked

                switch(entries[index, 2])
                {
                    case TransactionTypes.Deposit.ToString():
                        rbDeposit.Checked = true;
                        break;
                    case TransactionTypes.Withdrawal.ToString():
                        rbWithdrawal.Checked = true;
                        break;
                    default:
                        rbServiceFee.Checked = true;
                        break;
                }
            }
        }
    }
}

【问题讨论】:

  • 不要在case语句中使用ToString;而是将entries[index, 2] 转换为事务类型,然后在没有ToString 的情况下进行切换。

标签: c# multidimensional-array switch-statement


【解决方案1】:

您应该抛弃多维数组,而使用类型化列表或数组。

当数据属于同一类型时,多维数组很有用,你的它几乎是一切。所以我们反过来会上课。

public class Entry
{
   public Entry(decimal amount, DateTime date, TransactionType transactionType, bool cleared)
   {
      Amount = amount;
      Date = date;
      TransactionType = transactionType;
      Cleared = cleared;
   }

   public decimal Amount { get; set; }
   public DateTime Date { get; set; }
   public TransactionType TransactionType { get; set; }
   public bool Cleared { get; set; }

}

给定

// now an array of Entry
private Entry[] entries = new Entry[20];

用法

要更新更新条目

// add / update and array slot
entries[row] = new Entry(amount, DateTime.Parse(txtDate),transactionType,chkCleared.Checked);

你的开关应该是这样的

switch (entries[index].TransactionType)
{
   case TransactionTypes.Deposit:
      rbDeposit.Checked = true;
      break;
   case TransactionTypes.Withdrawal:
      rbWithdrawal.Checked = true;
      break;
   default:
      rbServiceFee.Checked = true;
      break;
}

注意:这并不是一个工作示例,也不是为了让您的代码工作,它只是向您展示如何使用类型数组,让您的生活更轻松,并显示切换的潜力,而无需将您的字符串解析回枚举

或者你也可以使用Enum.Parse

转换名称或数值的字符串表示形式一 或多个枚举常量到一个等效的枚举对象。

var tranType = (TransactionTypes)Enum.Parse(typeof(TransactionTypes), entries[index, 2]);
switch(tranType)

【讨论】:

    【解决方案2】:

    您面临的错误与多维数组无关。这是由于在案例逻辑中使用了 runtime 值。

    您不能使用case TransactionTypes.Withdrawal.ToString() 作为案例陈述。最后一部分ToString() 是一个函数调用。即使它总是返回相同的值,编译器也不知道。它总是认为case TransactionTypes.Withdrawal.ToString() 的值在编译时 是未知的,因此存在runtime 错误的潜在风险,因此无法编译。

    您可能想知道拥有 runtime 值有什么害处。考虑以下代码 -

    switch (entries[index].TransactionType)
    {
       case TransactionTypes.Withdrawal.ToString()://only known at wexecution not before that
          throw a;
          break;
       case TransactionTypes.Withdrawal.ToString()://only known at wexecution not before that
          throw b;
          break;
       default:
          break;
    }
    

    这两种情况具有相同的值。但是编译器无法确定它们,除非它执行代码,并且当它尝试这样做时,会在决定中发生冲突。为了保存此类失败的代码,禁止在 switch-case 中具有动态值,这就是该异常的原因。

    将来,编译器可能会更智能地根据代码确定正确的分支,但现在我们被固定在案例的常量值上。所以删除函数调用或使用Enum.Parse 来获取枚举值并进行检查。像这样的 -

    if(Enum.TryParse<TransactionTypes>(entries[index, 2], true, out TransactionTypes parsed){
        switch (parsed)
        {
           case TransactionTypes.Deposit://constant value, no issue
              rbDeposit.Checked = true;
              break;
           case TransactionTypes.Withdrawal://constant value, no issue
              rbWithdrawal.Checked = true;
              break;
           default:
              rbServiceFee.Checked = true;
              break;
        }
    }
    else{
        throw new Exception("Unknonw enum string");
    }
    

    在此处https://docs.microsoft.com/en-us/dotnet/api/system.enum.tryparse?view=netframework-4.8 获取有关Enum.ParseEnum.TryParse 的更多详细信息

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-05
      • 1970-01-01
      相关资源
      最近更新 更多