Я сижу здесь со школьным проектом, в котором не могу разобраться; Я должен создать консольное приложение, которое позволит пользователю ввести количество продавцов для гипотетической компании. Информация о продавцах включает их имя, район и количество проданных товаров. Затем продавцов нужно отсортировать по разным уровням в зависимости от количества проданных товаров. Наконец, продавцы должны быть выведены на консоль. Печать должна выполняться по одному уровню за раз, если, например, два продавца достигли уровня 1, а один – уровня 2, консоль должна выглядеть примерно так:
John Johnsson, SomeDistrict, продано 33 шт. Мары Мара, SomeOtherDistrict, продано 40 шт. 2 продавца достигли уровня 1
Джуди Джаггернат, другой район, продано 67 шт. 1 продавец достиг уровня 2
И именно та часть печати, о которой идет речь, доставляет мне неприятности. Когда пользователь вводит информацию, создается новый объект класса продавцов, который сохраняется в массиве продавцов. Затем проверяется количество товаров, проданных каждым продавцом, и каждому продавцу присваивается уровень. Затем массив сортируется с помощью пузырьковой сортировки, чтобы иметь продавца с наименьшим объемом продаж в 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 новых массива для разных уровней. Затем я перебираю массив со всеми продавцами и помещаю продавцов в массивы в соответствии с количеством проданных товаров. Затем я проверяю, пусты ли массивы уровней. Если это не так, я перебираю их, распечатывая имя, район и товары, проданные для каждого продавца. Наконец, также распечатайте количество продавцов на каждом уровне. При запуске программы получаю ошибку в строке
Console.WriteLine("Name: " + level1[i].name);
Высказывание «System.NullReferenceException было брошено» «Ссылка на объект не установлена на экземпляр, если это объект».
Я бы предположил, что это означает, что level1[i].name не ссылается на объект, но я действительно не знаю, как оттуда идти... Буду очень признателен за любые советы или указатели!
Вы получаете System.NullReferenceException
, потому что вы инициализируете массивы уровней той же длины, что и массив продавцов, но вы добавляете продавцов в массивы уровней только на основе их уровня.
Таким образом, в массивах уровней будут неинициализированные null
элементы, и когда вы попытаетесь получить доступ к свойству name нулевого элемента, вы получите исключение, потому что вы пытаетесь прочитать свойство отсутствующего элемента.
Чтобы исправить это, вы можете использовать List<Salesman>
вместо Salesman[]
. List<T> — это общий динамический массив, и вы можете перебирать его элементы таким же образом:
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 может содержать только значения из списка [1, 2, 3, 4]
, вы можете хранить уровни в List
списка List<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}");
}
Большое спасибо за объяснение и советы! Я попытался изучить ошибку перед публикацией, но мне было трудно применить то, что было сказано, к моей проблеме, но теперь я ясно это понимаю!
@pinkRobot435, если вы выдаете этот код как свой, вы совершаете плагиат.
Где пузырьковая сортировка? Сначала отсортируйте массив, затем выполните цикл по массиву со счетчиками для подсчета каждого уровня и распечатайте результат того же цикла.
// 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
//...
... это строки, которые вы должны заполнить.
Большое спасибо за ваш ответ! Я не включил пузырьковую сортировку, так как сохранил ее в отдельном методе и хотел быть максимально кратким. Я на самом деле попробовал ваш подход, прежде чем придумать решение, которое у меня есть раньше, но не мог понять, как заставить его работать... Приятно видеть, как это можно сделать, спасибо
В ответе Вадима подробно описано, почему ваш код не работает. Он предложил способ решить проблему с помощью List
s. Я бы тоже пошел по этому пути.
С другой стороны, ваш подход был правильным, но не очень эффективным и имеет несколько ловушек для себя (как упомянул Вадим, вы создаете 4-уровневые массивы с одинаковым размером общего количества продавцов, затем вы назначаете их каждому уровню через i
, оставив несколько null
пробелов). Если вы хотите, чтобы ваш подход работал, в цикле печати for
перед получением level1[i].name
убедитесь, что level1[i]
не является null
.
Если вы используете IDE, я бы порекомендовал вам поставить точку останова внутри цикла for и посмотреть содержимое level1
.
Удачи в обучении!
Большое спасибо за Ваш ответ! Я понимаю, что мое решение довольно неэффективно... Сначала я попытался найти подход, похожий на то, что предложил Стром, но не смог заставить его работать и вместо этого решил сделать что-то более простое. Но я думаю, в этом вся прелесть обучения, я очень рад, что понял ошибку
Может оказаться полезным следующее: IComparable и IComparer