Indentation
Your indentation is off here (it may just be a copy/paste error, however it's worth mentioning, remember consistency is key):
public void mainMenu(BookList b)
{
Naming
Public methods should follow the Pascal naming convention, so mainMenu
should really be MainMenu
, addBook
=>AddBook
etc.
InnerLoop
The inner loop under the case 1 is weird and the way it uses recursion to call the menu method again may cause problems with the stack if you add too many books in a single session.
Unhandled menu items
Your menu has 7 items in it, however you only handle items up to 5. This means 6 is actually the same as 7 in that it exits the menu. As there is no validation around the selected menu options, any other entry from the user (for example 'help') would also result in exiting the menu.
Consistently Clear
After you add a book, then the screen is cleared before adding another / before re-displaying the main menu. The main menu doesn't contain a clear at the top though, meaning that the first time it is displayed it may or may not be on a clear console window (depending on what happened before mainMenu
was called).
Separation of concerns
Generally speaking, you want to separate your user interactions from the core logic of your application. From your comment this doesn't appear to be the case in your application "addBook()
asks for name, author, release date etc". This couples your logic to the user interface and makes it difficult to change in the future. If you want to change the user interface to a web page, or WPF you have to go in and change the behaviour of your BookList
. Try to think about how you can separate out your application into different areas of responsibility (user interaction being one of them) so that it is easier to maintain going forward. A step towards doing this is breaking down your code into methods with meaningful names that describe what each method does.
It is also a good idea to think about how you might want to drive the code if you didn't have a user interface (unit testing can be a good way to practice this). This can lead you to thinking of a BookList
in a slightly different way, so rather than having void addBook()
, you might have BookIdentifier AddBook(Book newBook)
. Then deleteBook()
might become DeleteBook(BookIdentifier)
etc. Where Book
represents information like 'Author, Release Date' etc and BookIdentifier
is some unique book identifer (for example its ISBN, or a unique catalog id etc).
Numbers for selection
One of the problems with using numbers for menus is that it can create a maintenance overhead. If you decided to add another menu item 'See list of borrowed books', then you probably wouldn't want it to be displayed after exit so you would make it number 7, and have to update the text for '7. Exit' and update the case statement that handles that selection. This can be error prone. @t3chb0t has provided a good solution to this. I've included another alternative, largely for contrast below.
First I create a simple class for holding details about a menu option:
public class MenuOption
{
public MenuOption(string itemText, Action itemHandler, bool isExitOption = false)
{
ItemText = itemText;
ItemHandler = itemHandler;
IsExitOption = isExitOption;
}
public string ItemText { get; }
public Action ItemHandler { get; }
public bool IsExitOption { get; }
}
This holds three pieces of information. The text that describes the menu option, an Action
(something to do when the option is selected) and a flag to indicate if this action represents the exit menu selection (this defaults to false
for no).
I then create a helper method to build a list of menu items. Notice that the action in most cases is simply the name of the method on bookList
to invoke, with the exception of "Exit":
static List<MenuOption> BuildMainMenu(BookList bookList)
{
return new List<MenuOption> { new MenuOption("Add a book.", bookList.addBook),
new MenuOption("Delete book.", bookList.deleteBook),
new MenuOption("Borrow a book.", bookList.borrowBook),
new MenuOption("Exit", ()=>Console.WriteLine("Goodbye"), true)
};
}
And another helper to display the list of items. This loops round the built list, printing each item, along with its selection number, so if the items change order in the list, their actions will still match their selection:
private static void DisplayMainMenu(List<MenuOption> options)
{
Console.Clear();
Console.WriteLine("Welcome to Library.");
Console.WriteLine("What do you want to do?");
int optionCount = 1;
foreach (var option in options)
{
Console.WriteLine($"{optionCount++}.{option.ItemText}");
}
}
One more helper to get a selection from the user:
private static MenuOption GetMenuSelection(List<MenuOption> options)
{
int selection;
do
{
string userChoice = Console.ReadLine();
if (int.TryParse(userChoice, out selection) &&
selection > 0 &&
selection <= options.Count)
{
return options[selection - 1];
}
Console.WriteLine("Sorry, Try again");
}
while (true);
}
Note that the above has some error checking to make sure a valid selection is picked by the user. The MainMenu
function then simply becomes a loop that continues until the user selects the exit option:
public void MainMenu(BookList bookList)
{
bool exitMenu = false;
var menu = BuildMainMenu(bookList);
while (!exitMenu) {
DisplayMainMenu(menu);
var menuChoice = GetMenuSelection(menu);
menuChoice.ItemHandler?.Invoke();
exitMenu = menuChoice.IsExitOption;
}
}