根据对象属性值对对象进行排序时出现问题

Problem sorting objects based on value of its properties

提问人:pinkRobot435 提问时间:2/19/2023 更新时间:2/19/2023 访问量:70

问:

我坐在这里,有一个我似乎无法弄清楚的学校项目;我将创建一个控制台应用程序,让用户为一家假设的公司输入一些销售人员。有关销售人员的信息包括他们的姓名、地区和销售商品的数量。然后,根据销售商品的数量将销售人员分为不同的级别。最后,销售人员将被打印到控制台上。打印应该在一次一级进行,例如,如果两个销售人员达到一级,一个达到二级,控制台应如下所示:

John Johnsson, someDistrict, 已售出 33 件商品 Mary Mara, someOtherDistrict, 已售出 40 件商品 2名业务员达到1级

Judy Juggernut,另一个区,售出 67 件商品 1名业务员达到2级

正是有问题的打印部分给我带来了麻烦。当用户输入信息时,将创建推销员类的新对象并将其存储在推销员数组中。然后检查每个推销员的销售商品数量,并为每个推销员分配一个级别。然后使用 bubblesort 对数组进行排序,以使销售人员在 salesmanArray[0] 上销售量最少,依此类推。

在将结果打印到控制台之前,一切正常。我试图为它编写一个方法:

    public static void sortering(Salesman[] salesmenArray)
    {
        Salesman[] level1 = new Salesman[salesmenArray.Length];
        Salesman[] level2 = new Salesman[salesmenArray.Length];
        Salesman[] level3 = new Salesman[salesmenArray.Length];
        Salesman[] level4 = new Salesman[salesmenArray.Length];

        for (int i = 0; i < salesmenArray.Length - 1; i++)
        {
            if (salesmenArray[i].level == 1)
            {
                level1[i] = salesmenArray[i];

            } else if (salesmenArray[i].level == 2)
            {
                level2[i] = salesmenArray[i];

            } else if (salesmenArray[i].level == 3)
            {
                level3[i] = salesmenArray[i];

            } else if (salesmenArray[i].level == 4)
            {
                level4[i] = salesmenArray[i];
            }
        }

        if (level1.Length != 0)
        {
            for (int i = 0; i < level1.Length - 1; i++)
            {
                Console.WriteLine("Name: " + level1[i].name);
                Console.WriteLine("District: " + level1[i].district);
                Console.WriteLine("Items sold: " + level1[i].itemsSold);
            }
            Console.WriteLine("" + (level1.Length - 1) + " sellers have reached level 1");
        }
            //Same thing for level 2, 3 and 4
    }

我正在尝试做的是用于不同级别的 4 个新数组。然后,我与所有销售人员一起遍历阵列,并根据销售商品的数量将销售人员放入阵列中。然后,我检查level数组是否为空。如果不是,我会循环播放它们,打印出每个推销员的姓名、地区和销售的物品。最后,还要打印出每个级别有多少卖家。运行程序时,我收到一个错误

Console.WriteLine("Name: " + level1[i].name);

说“System.NullReferenceException已被抛出”对象引用未设置为实例(如果是对象)。

我认为这意味着 level1[i].name 没有引用对象,但我真的不知道如何从那里开始......任何建议或指示将不胜感激!

C# 数组对象 NullReferenceException

评论

2赞 Tu deschizi eu inchid 2/19/2023
以下内容可能会有所帮助:IComparableIComparer

答:

2赞 Vadim Martynov 2/19/2023 #1

您得到一个,因为您正在使用与 salesmen 数组相同的长度初始化 level 数组,但您只是根据他们的级别将 salesmen 添加到级别数组中。System.NullReferenceException

因此,level 数组中不会有初始化的元素,当您尝试访问 null 元素的 name 属性时,由于您尝试读取 absent 元素的属性,因此会出现异常。null

要解决此问题,您可以使用代替 .List<T> 是一个通用动态数组,您可以采用相同的方式遍历其项:List<Salesman>Salesman[]

public static void sortering(Salesman[] salesmenArray)
{
    var level1 = new List<Salesman>();
    var level2 = new List<Salesman>();
    var level3 = new List<Salesman>();
    var level4 = new List<Salesman>();

    for (int i = 0; i < salesmenArray.Length; i++)
    {
        if (salesmenArray[i].level == 1)
        {
            level1.Add(salesmenArray[i]);
        }
        else if (salesmenArray[i].level == 2)
        {
            level2.Add(salesmenArray[i]);
        }
        else if (salesmenArray[i].level == 3)
        {
            level3.Add(salesmenArray[i]);
        }
        else if (salesmenArray[i].level == 4)
        {
            level4.Add(salesmenArray[i]);
        }
    }

    if (level1Count > 0)
    {
        for (int i = 0; i < level1.Count; i++)
        {
            Console.WriteLine("Name: " + level1[i].name);
            Console.WriteLine("District: " + level1[i].district);
            Console.WriteLine("Items sold: " + level1[i].itemsSold);
        }
        Console.WriteLine("" + level1Count + " sellers have reached level 1");
    }
    //Same thing for level 2, 3 and 4
}

这里有一些其他的改进,然后你可以用你的代码来做。例如,如果 Salesman.level 可能只包含列表中的值,则可以将级别存储在 of 或数组中,并以更简单的方式添加项目。此外,字符串插值是一种更简单、更快、更易读的字符串连接语法。[1, 2, 3, 4]ListList<Salesman>List<Salesman>

// here we creates a new array of lists and initialize it with 4 empty lists of Salesman
var levels = new List<Salesman>[] 
{
    new List<Salesman>(),
    new List<Salesman>(),
    new List<Salesman>(),
    new List<Salesman>()
};

foreach(var salesmen in salesmenArray)
{
    // (salesmen.level - 1)-th list stores salesmen with that level
    levels[salesmen.level - 1].Add(salesmen);
}

// you can iterate salesmen of all levels with nested loops 
for(int level = 0; level < levels.Lenth; level++)
{
    foreach(var salesman in levels[level])
    {
        Console.WriteLine($"Name: {salesman.name}");
        Console.WriteLine($"District: {salesman.district}");
        Console.WriteLine($"Items sold: {salesman.itemsSold}");
    }

    // Count property gets the number of elements contained in the List<T> so you don't need to decrement this value for display the number of salesmen with this level 
    Console.WriteLine($"{levels[level].Count} sellers have reached level {level + 1}");
}

最后,还有一种有趣的机制来操作 .NET 中的集合,称为 LINQ。可以使用 LINQ 语法来选择、筛选、分组和聚合数据。LINQ是可读的高效和强大的工具。下面是使用 LINQ 重写的代码示例:

foreach(var group in salesmenArray
    .GroupBy(salesman => salesman.level)
    .OrderBy(groups => groups.Key))
{
    foreach(var salesman in group)
    {
        Console.WriteLine($"Name: {salesman.name}");
        Console.WriteLine($"District: {salesman.district}");
        Console.WriteLine($"Items sold: {salesman.itemsSold}");
    }
    Console.WriteLine($"{group.Count()} sellers have reached level {group.Key}");
}

评论

0赞 pinkRobot435 2/19/2023
非常感谢您的解释和提示!我尝试在发布之前研究错误,但发现很难将所说的内容应用于我的问题,但现在清楚地理解了它!
0赞 2/19/2023
@pinkRobot435,如果你把这些代码当作你的代码上交,你就是在抄袭。
1赞 user10316640 2/19/2023 #2

气泡在哪里排序?首先对数组进行排序,然后使用计数器遍历数组以计算每个级别并打印同一循环的输出。

   // bubble sort
   for (int i = 0; i < salesmenArray.Length; i++)
      for (int j = 0; j < salesmenArray.Length - 1; j++)
         if(salesmenArray[j].itemsSold > salesmenArray[j+1].itemsSold)
         {
            //swap positions
            //...
         }
   int counter = 0;
   int lastLevel = 1; //if 1 is the min level
   for (int i = 0; i < salesmenArray.Length; i++)
   {
       if(salesmenArray[j].level != lastLevel)
       {
          //print summary
          //...
         counter = 0; //reset counter
       }
       // print detail lines
       Console.WriteLine("Name: " + level1[i].name);
       Console.WriteLine("District: " + level1[i].district);
       Console.WriteLine("Items sold: " + level1[i].itemsSold);

       counter++;
   }
   //print final summary for last level
          //...

这。。。是供您填写的行。

评论

0赞 pinkRobot435 2/19/2023
非常感谢您的回答!我没有包含气泡排序,因为我将其保存在一个单独的方法中,并希望尽可能简洁。在提出我现在拥有的解决方案之前,我实际上尝试了您的方法,但不太清楚如何让它工作......很高兴看到它是如何做到的,谢谢
1赞 Jazz. 2/19/2023 #3

Vadim 的回答详细说明了代码失败的原因。他提出了一种通过s解决问题的方法。我也会走那条路。List

另一方面,你的方法很有效,但不是很有效,并且有一些陷阱(正如 Vadim 提到的,你正在创建 4 个级别数组,其大小与销售人员总数相同,然后你通过 将它们分配给每个级别,留下一些空白)。如果你想让你的方法工作,在打印循环中,在得到之前,检查不是。inullforlevel1[i].namelevel1[i]null

如果您使用的是 IDE,我建议您在 for 循环中放置一个断点并查看 .level1

祝你好运!

评论

1赞 pinkRobot435 2/19/2023
非常感谢您的回复!我意识到我的解决方案效率很低......我首先试图找出一种类似于 Strom 建议的方法,但无法完全让它发挥作用,而是决定采用更简单的东西。但我想这就是学习的美妙之处,我很高兴能够理解这个错误