如何使我的C#自定义类数组正确跟踪成员元素?

huangapple go评论77阅读模式
英文:

How do I get my C# custom class array to correctly track member elements?

问题

我正在尝试实现以下功能 - 当用户在ProductsListCBX中输入条形码时,应用程序应查找该产品并加载产品信息。我的自定义类有一个字段ItemCount,我有一个数组TransactionRecord[50],它以数组的形式存储每个交易的信息(ItemCount, ItemName, ItemUnitCost)。我的问题是ItemCount字段似乎保留了上次交易的ItemCount,而它应该在数组的每个新记录中初始化为ItemCount = 0。项目计数只是按顺序递增,当我循环遍历数组时(显示它们时显示为1, 2, 3, ...,而应该是默认的1, 1, 1, ...)。当添加新交易时,如果条形码与已存在于某个交易中的条形码匹配,则应用程序应仅增加该交易的ItemCount。我在我的代码中做错了吗?请协助,我已经提供了以下代码。

using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Printing;

namespace MyApp
{
    public partial class POSHubForm : Form
    {
        public POSHubForm(string username, string org)
        {
            InitializeComponent();
            UsernameLBL.Text = username;
            CompanyLBL.Text = org;
        }

        Transaction[] TransactionRecord = new Transaction[50];
        readonly Product SaleItem = new Product();
        string saleString = String.Empty;
        string MasterSaleString = String.Empty;
        string footerString = String.Empty;
        double MasterCost = 0;
        double totalCost = 0;
        int salesCount = 0;
        int RecordCount = 0;
        int AllTempTotal = 0;
        int TempTotal = 0;
        int TotalSalesToday = 0;

        private void CreateTransactionRecord()
        {         
            if (ProductListCBX.Text != String.Empty)
            {
                try
                {
                    string itemDataLocation = "D:\\MyApp\\Barcodes\\" + ProductListCBX.Text + ".txt";
                    TransactionRecord[RecordCount] = new Transaction();
                    SaleItem.LoadProduct(itemDataLocation);

                    saleString += SaleItem.GetPaddedName() + "E" + String.Format("{0:0.00}", SaleItem.GetPrice()) + "\n";
                    MasterSaleString += SaleItem.GetPaddedName() + "E" + String.Format("{0:0.00}", SaleItem.GetPrice()) + "\n";
                    ProductInfoLBL.Text = SaleItem.GetName();
                    totalCost += SaleItem.GetPrice();
                    MasterCost += SaleItem.GetPrice();
                    TotalCostLBL.Text = "E" + String.Format("{0:0.00}", totalCost);
                    ItemCostLBL.Text = "E" + String.Format("{0:0.00}", SaleItem.GetPrice());
                    salesCount++;
                    CountBtn.Text = Convert.ToString(salesCount);

                    for (int k = 0; k <= RecordCount; k++)
                    {
                        if (Convert.ToInt64(ProductListCBX.Text) == TransactionRecord[k].GetBarCode())
                        {
                            TransactionRecord[k].IncreaseItemCount();
                        }
                        else
                        {
                            TransactionRecord[RecordCount].IncreaseItemCount();
                            TransactionRecord[RecordCount].SetBarCode(Convert.ToInt64(ProductListCBX.Text));
                            TransactionRecord[RecordCount].SetUnitPrice(Convert.ToDouble(SaleItem.GetPrice()));
                            TransactionRecord[RecordCount].SetItemName(SaleItem.GetName());
                            TransactionRecord[RecordCount].SetTotalPrice();
                        }
                    }

                    string Record = String.Empty;
                    string Items = String.Empty;

                    for (int n = 0; n <= RecordCount; n++)
                    {
                        Record += TransactionRecord[n].CreateSaleRecord();
                        Items += Convert.ToString(TransactionRecord[n].GetItemCount()) + ", ";
                    }

                    MessageBox.Show(Record + "\n\n" + Items);
                    RecordCount++;

                    ItemDescriptionTBX.Text += "  > " + SaleItem.GetName().PadRight(30) + "E" + String.Format("{0:0.00}", SaleItem.GetPrice()) + "\r\n";
                }
                catch (FileNotFoundException)
                {
                    MessageBox.Show("您输入的产品不存在");
                }
                catch (Exception x)
                {
                    MessageBox.Show(x.Message);
                }
            }
            else
            {
                MessageBox.Show("物品列表不能为空。键入、浏览或滚动项目列表以添加销售产品。");
            }

            ProductListCBX.Text = String.Empty;
        }
    }
}

//编辑 - 这是Transaction类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyApp
{
    class Transaction
    {
        private long ItemBarCode;
        private double ItemUnitPrice = 0;
        private double ItemTotalPrice = 0;
        private string ItemName = String.Empty;
        private int ItemCount = 0;

        public long GetBarCode()
        {
            return ItemBarCode;
        }

        public string GetItemName()
        {
            return ItemName;
        }

        public int GetItemCount()
        {
            return ItemCount;
        }

        public double GetUnitPrice()
        {
            return Convert.ToDouble(ItemCount * ItemUnitPrice);
        }

        public double GetTotalPrice()
        {
            return Convert.ToDouble(ItemCount * ItemUnitPrice);
        }

        public string CreateSaleRecord()
        {
            return Convert.ToString(ItemCount).PadRight(3) + " x " + ItemName.PadRight(50) + "- E" + String.Format("{0:0.00}", GetTotalPrice()) + "\n";
        }

        public void ReverseItem()
        {

        }

        public void IncreaseItemCount()
        {
            ItemCount++;
        }

        public void DecreaseItemCount()
        {
            ItemCount--;
        }

        public void SetBarCode(long Code)
        {
            ItemBarCode = Code;
        }

        public void SetItemName(string Name)
        {
            ItemName = Name;
        }

        public void SetUnitPrice(double Price)
        {
            ItemUnitPrice = Price;
        }

        public void SetTotalPrice()
        {
            ItemTotalPrice = Convert.ToDouble(ItemCount * ItemUnitPrice);
        }
    }
}
英文:

I'm trying to achieve the following - When a user enters a barcode in ProductsListCBX, the app should look up the product and load the product info. My custom class has a field ItemCount, and I have an array TransactionRecord[50] that stores each transaction's info (ItemCount, ItemName, ItemUnitCost) as a record of the array. My issue is the ItemCount field seems to be retaining the ItemCount from the last transaction when it should initialize each new record of the array with an ItemCount = 0. The item count just increases sequentially as I loop through the array (displaying them gives 1, 2, 3, ... when it should have a default of 1, 1, 1, ... ). When adding a new transaction, if the barcode matches a barcode that's already in one of the transactions, then the app should just increase the ItemCount for that transaction. Am I doing something wrong with my code? Please assist, I've provided the code below.

using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Printing;
namespace MyApp
{
public partial class POSHubForm : Form
{
public POSHubForm(string username, string org)
{
InitializeComponent();
UsernameLBL.Text = username;
CompanyLBL.Text = org;
}
Transaction[] TransactionRecord = new Transaction[50];
readonly Product SaleItem = new Product();
string saleString = String.Empty;
string MasterSaleString = String.Empty;
string footerString = String.Empty;
double MasterCost = 0;
double totalCost = 0;
int salesCount = 0;
int RecordCount = 0;
int AllTempTotal = 0;
int TempTotal = 0;
int TotalSalesToday = 0;
private void CreateTransactionRecord()
{         
if (ProductListCBX.Text != String.Empty)
{
try
{
string itemDataLocation = &quot;D:\\MyApp\\Barcodes\\&quot; + ProductListCBX.Text + &quot;.txt&quot;;
TransactionRecord[RecordCount] = new Transaction();
SaleItem.LoadProduct(itemDataLocation);
saleString += SaleItem.GetPaddedName() + &quot;E&quot; + String.Format(&quot;{0:0.00}&quot;, SaleItem.GetPrice()) + &quot;\n&quot;;
MasterSaleString += SaleItem.GetPaddedName() + &quot;E&quot; + String.Format(&quot;{0:0.00}&quot;, SaleItem.GetPrice()) + &quot;\n&quot;;
ProductInfoLBL.Text = SaleItem.GetName();
totalCost += SaleItem.GetPrice();
MasterCost += SaleItem.GetPrice();
TotalCostLBL.Text = &quot;E&quot; + String.Format(&quot;{0:0.00}&quot;, totalCost);
ItemCostLBL.Text = &quot;E&quot; + String.Format(&quot;{0:0.00}&quot;, SaleItem.GetPrice());
salesCount++;
CountBtn.Text = Convert.ToString(salesCount);
for (int k = 0; k &lt;= RecordCount ; k++)
{
if(Convert.ToInt64(ProductListCBX.Text) == TransactionRecord[k].GetBarCode())
{
TransactionRecord[k].IncreaseItemCount();
}
else
{
TransactionRecord[RecordCount].IncreaseItemCount();
TransactionRecord[RecordCount].SetBarCode(Convert.ToInt64(ProductListCBX.Text));
TransactionRecord[RecordCount].SetUnitPrice(Convert.ToDouble(SaleItem.GetPrice()));
TransactionRecord[RecordCount].SetItemName(SaleItem.GetName());
TransactionRecord[RecordCount].SetTotalPrice();
}
}
string Record = String.Empty;
string Items = String.Empty;
for (int n = 0; n &lt;= RecordCount; n++)
{
Record += TransactionRecord[n].CreateSaleRecord();
Items += Convert.ToString(TransactionRecord[n].GetItemCount()) + &quot;, &quot;;
}
MessageBox.Show(Record + &quot;\n\n&quot; + Items);
RecordCount++;
ItemDescriptionTBX.Text += &quot;  &gt; &quot; + SaleItem.GetName().PadRight(30) + &quot;E&quot; + String.Format(&quot;{0:0.00}&quot;, SaleItem.GetPrice()) + &quot;\r\n&quot;;
}
catch (FileNotFoundException)
{
MessageBox.Show(&quot;The product you entered does not exist&quot;);
}
catch (Exception x)
{
MessageBox.Show(x.Message);
}
}
else
{
MessageBox.Show(&quot;Item list cannot be empty. Type, browse, or scroll the item list to add products for a sale.&quot;);
}
ProductListCBX.Text = String.Empty;
}
}
}
//EDIT - This is the Transaction class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyApp
{
class Transaction
{
private long ItemBarCode;
private double ItemUnitPrice = 0;
private double ItemTotalPrice = 0;
private string ItemName = String.Empty;
private int ItemCount = 0;
public long GetBarCode()
{
return ItemBarCode;
}
public string GetItemName()
{
return ItemName;
}
public int GetItemCount()
{
return ItemCount;
}
public double GetUnitPrice()
{
return Convert.ToDouble(ItemCount * ItemUnitPrice);
}
public double GetTotalPrice()
{
return Convert.ToDouble(ItemCount * ItemUnitPrice);
}
public string CreateSaleRecord()
{
return Convert.ToString(ItemCount).PadRight(3) + &quot; x &quot; + ItemName.PadRight(50) + &quot;- E&quot; + String.Format(&quot;{0:0.00}&quot;, GetTotalPrice()) + &quot;\n&quot;;
}
public void ReverseItem()
{
}
public void IncreaseItemCount()
{
ItemCount++;
}
public void DecreaseItemCount()
{
ItemCount--;
}
public void SetBarCode(long Code)
{
ItemBarCode = Code;
}
public void SetItemName(string Name)
{
ItemName = Name;
}
public void SetUnitPrice(double Price)
{
ItemUnitPrice = Price;
}
public void SetTotalPrice()
{
ItemTotalPrice = Convert.ToDouble(ItemCount * ItemUnitPrice);
}
}
}
</details>
# 答案1
**得分**: 0
以下是您的类的一些调整。由于没有太多的验证逻辑,我们肯定可以将您的get和set方法合并为自动属性。这使得类更加紧凑且更容易使用。
```csharp
public class Transaction
{
public long ItemBarCode { get; set; }
public string ItemName { get; set;} = string.Empty;
public uint ItemCount { get; set; } = 1;
//这两者可以是decimal类型,也可以是double,随您决定
public double ItemUnitPrice { get; set; }
public double ItemTotalPrice => ItemCount * ItemUnitPrice; //这是一个只读属性,可以将其视为getter方法。这是一个计算值,没有真正需要setter方法
//通过格式化插值字符串,可以使下面的方法更加清晰,详见:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
public string CreateSaleRecord() => Convert.ToString(ItemCount).PadRight(3) + " x " + ItemName.PadRight(50) + "- E" + String.Format("{0:0.00}", GetTotalPrice()) + "\n";
public void ReverseItem()
{
//未提供内容,保持原样
}
}

让我们总结一下关键更改 public uint ItemCount { get; set; } = 1;

  1. 您可以通过 transaction.ItemCount 访问这个属性。
  2. 现在可以使用 transaction.ItemCount++transaction.ItemCount-- 来增加和减少计数。由于这是无符号整数 uint,不应该减到负值,因此请确保有服务逻辑来阻止这种情况发生。
  3. 注意末尾的 = 1。这确保了在创建新对象时,此属性将初始化为1。我不确定为什么您最初将值初始化为0,如果您更喜欢从1开始,可以这样做。

话虽如此,这实际上应该使用类似 Dictionary<long, Transaction> 来正确查找确切的记录。您当前正在使用全局变量 RecordCount 来跟踪数组中的当前位置,但这是非常不必要的复杂性。

我对逻辑有点困惑,但似乎您正在搜索记录以查看该产品是否存在,如果存在,则增加计数。如果不存在,则简单地“添加”一个新事务(实际上是覆盖了记录在 RecordCount 索引处的内容)。简化所有这些:首先尝试读取和更新记录,如果失败,则将记录添加到字典中。

Dictionary<long, Transaction> records = new(); //键 = 条形码
//在这里添加记录

if (long.TryParse(ProductListCBX.Text, out long textCode))
{
    if (records.ContainsKey(textCode)) 
    {
        records[textCode].ItemCount++;
    }
    else 
    {
        records.Add(new Transaction()
        {
            //item count is already init to 1
            ItemBarCode = textCode,
            ItemUnitPrice = SaleItem.Price,
            ItemName = SaleItem.Name,
            //total price is available on-demand, no need to set
        });
    }
}
else
{
    //处理无法解析的情况
}

非常重要的是,字典在查找操作上具有常数时间复杂度,而不是在每次需要更新记录集合时执行搜索操作。永远不需要全局变量手动跟踪数组中当前要处理的记录。如果条形码保证是唯一的,那么它是作为字典键的完美候选者。

英文:

Here are some adjustments to your class. Since there isn't much validation logic we can definitely collapse your get and set methods into auto properties. This makes the class much more compact and easier to use.

public class Transaction
{
    public long ItemBarCode { get; set; }
    public string ItemName { get; set;} = string.Empty;
    public uint ItemCount { get; set; } = 1;

    //these both can be decimal type, or double, your call
    public double ItemUnitPrice { get; set; }
    public double ItemTotalPrice =&gt; ItemCount * ItemUnitPrice; //this is a readonly property, imagine this as a getter method. This is a computed value, there is no real need for a setter method

    //the following method could be made a bit cleaner by formatting interpolated strings, read: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
    public string CreateSaleRecord() =&gt; Convert.ToString(ItemCount).PadRight(3) + &quot; x &quot; + ItemName.PadRight(50) + &quot;- E&quot; + String.Format(&quot;{0:0.00}&quot;, GetTotalPrice()) + &quot;\n&quot;;

    public void ReverseItem()
    {
        //nothing provided, left as is
    }
}

So lets summarize the key change public uint ItemCount { get; set; } = 1;

  1. You can access this with transaction.ItemCount
  2. You can now increment with transaction.ItemCount++ and transaction.ItemCount--. This being an unsigned int uint it should never be decremented into negative values so be sure to have service logic to block that from happening
  3. Notice the = 1 at the end. This ensures that on construction of a new object, this property will initialize to 1. I am not sure why you originally initialize the value to 0 if you would prefer it to start at 1

Now that being said, this really should be using something like a Dictionary&lt;long, Transaction&gt; to properly lookup the exact record. You are currently using a global RecordCount variable to track the current place in the array but this is very needless complexity

I am a bit confused by the logic, but it seems as if you are searching the records to see if that product exists, and if so, increment the count. If not, then simply "add" a new transaction (by essentially clobbering whatever is at the RecordCount index). Simplify all of this: first attempt to read and update the record, and if not, add the record to the Dictionary.

Dictionary&lt;long, Transaction&gt; records = new(); //key = bar code
//add records here

if (long.TryParse(ProductListCBX.Text, out long textCode))
{
    if (records.ContainsKey(textCode)) 
    {
        records[textCode].ItemCount++;
    }
    else 
    {
        records.Add(new Transaction()
        {
            //item count is already init to 1
            ItemBarCode = textCode,
            ItemUnitPrice = SaleItem.Price,
            ItemName = SaleItem.Name,
            //total price is available on-demand, no need to set
        });
    }
}
else
{
    //Handle scenario if you could not parse
}

Very important to note that Dictionaries boast constant time complexity on lookups, vs executing a search operation every single time you need to make updates to the records collection. There will never be a need for a global variable that is manually tracking the current record being searched in the array also. That just sounds like future debugging hell and violates proper separation of responsibilities (the class containing RecordCount should not be tracking the current order being processed, at least not in the way done here). If that barcode is guaranteed to be unique then its a perfect candidate as a dictionary key

huangapple
  • 本文由 发表于 2023年3月15日 21:36:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75745483.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定