βœ… User defined Exception Handling

namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();
if (!File.Exists(filepath))
{
throw new FileDoesNotExistException(filepath);
}

TextFileReader.StreamTextFile(filepath);




}
}
}

namespace Workshop3.Exceptions
{
public class FileDoesNotExistException : Exception
{
public FileDoesNotExistException(string filepath) : base($"The filepath {filepath} does not exist! Press Enter to try again. ")
{
Console.ReadLine();
ReadTextFileMenu.ReadTextFile();
}
}
}
namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();
if (!File.Exists(filepath))
{
throw new FileDoesNotExistException(filepath);
}

TextFileReader.StreamTextFile(filepath);




}
}
}

namespace Workshop3.Exceptions
{
public class FileDoesNotExistException : Exception
{
public FileDoesNotExistException(string filepath) : base($"The filepath {filepath} does not exist! Press Enter to try again. ")
{
Console.ReadLine();
ReadTextFileMenu.ReadTextFile();
}
}
}
Why do i get a "An object reference is required for the non-static field, method, or property 'ReadTextFileMenu.ReadTextFile()' error?
88 Replies
Pobiega
Pobiegaβ€’3mo ago
because TextFileReader.StreamTextFile(filepath); looks like a static invocation when you invoke a static method, you do so by ClassName.MethodName() for an instance method, you need a class instance example:
var x = new MyClass();
x.MethodName();
var x = new MyClass();
x.MethodName();
Merineth πŸ‡ΈπŸ‡ͺ
Hmm I'm not sure i'm following. The method inside ReadTextFileMenu.cs named ReadTextFile. If the if statement triggers, it will throw an exception i created. Type out the message and then restart the method ReadTextFile() Static method and instance method are new concepts
Pobiega
Pobiegaβ€’3mo ago
why would it restart? I see no such code. and this code is unrelated to your error
Merineth πŸ‡ΈπŸ‡ͺ
I was hoping ReadTextFileMenu.ReadTextFile(); would make it restart
Pobiega
Pobiegaβ€’3mo ago
oh dear god I see now oh my this is very cursed. Congratulations, I've never seen this before πŸ˜„ okay, I see what you were trying to do, but using exceptions like this is not the way to do it. but no worries, we can quickly sort up the confusion first, an Exception should only contain information about the error that occured, it should not try to fix it (thats what the catch block is for)
Merineth πŸ‡ΈπŸ‡ͺ
Oh
Pobiega
Pobiegaβ€’3mo ago
second, never do input/output code inside a constructor, but especially not in an exception constructor You knew some C, right?
Merineth πŸ‡ΈπŸ‡ͺ
Is that the text inside base () ? Yeah i know C
Pobiega
Pobiegaβ€’3mo ago
no, thats just calling the base exception handler so you've heard about while and for, right?
Merineth πŸ‡ΈπŸ‡ͺ
Yea
Pobiega
Pobiegaβ€’3mo ago
great I'd say while is a fairly appropriate tool here you want to ask the user for a valid path while the current path is not valid does that make sense?
Merineth πŸ‡ΈπŸ‡ͺ
Yeah i think i know what you mean. While the input from the user isn't valid, redo it
Pobiega
Pobiegaβ€’3mo ago
throwing exceptions is usually the answer to the problem "I know there was an error, but I don't have the ability to handle it in my current scope. I should let someone else above me know there was a problem, and let them decide what to do" so for example, if the menuitem code is responsible for asking for the path, but the path is only validated inside the ReadFromTextFile method you made before, thats where an exception could be used "Oh, this method might throw, so I need to handle the path being invalid. And if it was, I'd probably just wanna ask again, so lets put it all in a loop"
Merineth πŸ‡ΈπŸ‡ͺ
Hmmm
namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();

while (!File.Exists(filepath))
{
throw new FileDoesNotExistException(filepath);
Console.WriteLine("Enter filepath..");
filepath = Console.ReadLine();
}

TextFileReader.StreamTextFile(filepath);
}
}
}
namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();

while (!File.Exists(filepath))
{
throw new FileDoesNotExistException(filepath);
Console.WriteLine("Enter filepath..");
filepath = Console.ReadLine();
}

TextFileReader.StreamTextFile(filepath);
}
}
}
I guess this was not how you meant? Considering i'm getting the same problem :catlaugh:
Pobiega
Pobiegaβ€’3mo ago
you'd need to remove the throw new... line here
Merineth πŸ‡ΈπŸ‡ͺ
Yeah i realized the code below becomes unreachable Ok the while loop works now. But it's not triggering the exception
Pobiega
Pobiegaβ€’3mo ago
well yeah, we removed the exception
Merineth πŸ‡ΈπŸ‡ͺ
The goal is to make a user defined exception for practice
Pobiega
Pobiegaβ€’3mo ago
oh okay
Merineth πŸ‡ΈπŸ‡ͺ
namespace Workshop3.Exceptions
{
public class FileDoesNotExistException : Exception
{
public FileDoesNotExistException(string filepath) : base($"The user tried to input something that isn't valid. They tried {filepath} ")
{
}
}
}
namespace Workshop3.Exceptions
{
public class FileDoesNotExistException : Exception
{
public FileDoesNotExistException(string filepath) : base($"The user tried to input something that isn't valid. They tried {filepath} ")
{
}
}
}
This would be my constructor for my exception?
Pobiega
Pobiegaβ€’3mo ago
yes
Merineth πŸ‡ΈπŸ‡ͺ
Inside the {} what would go there?
Pobiega
Pobiegaβ€’3mo ago
that error message should not include instructions to the user, btw exceptions should not be shown to users, they are intended for the programmer or an error log file
Merineth πŸ‡ΈπŸ‡ͺ
Ohh i see
Pobiega
Pobiegaβ€’3mo ago
most of the time, nothing. If your exception contains detailed information about the error, like an error code, or stuff like that, you might put it there for example:
public class MyFileException : Exception
{
public string FilePath { get; }

public MyFileException(string message, string filePath) : base(message)
{
FilePath = filePath;
}
}
public class MyFileException : Exception
{
public string FilePath { get; }

public MyFileException(string message, string filePath) : base(message)
{
FilePath = filePath;
}
}
Merineth πŸ‡ΈπŸ‡ͺ
I updated it. So something like this is more appropriate?
Pobiega
Pobiegaβ€’3mo ago
this would not be out of place at all in an exception based around files Sure, that could work. Its a bit verbose and "friendly", which is unusual for exceptions but it works A more common message might be "Specified path '{path}' does not exist" but what you wrote is fine too πŸ™‚
Merineth πŸ‡ΈπŸ‡ͺ
So now i include an if to check if the filepath does exist, and throw an exception?
Pobiega
Pobiegaβ€’3mo ago
from your previous thread, you used to have 2 methods right? the menu item, and your own stream-file-reader thing
Merineth πŸ‡ΈπŸ‡ͺ
Ah that was a different namespace
Pobiega
Pobiegaβ€’3mo ago
not working on that problem anymore? if you want to demonstrate proper usage of exceptions, I suggest you throw the exception in the file-reading method, and handle it in the menu item method exceptions are rarely (never?) used to handle errors within the same method that made them - because if you CAN do that, you can handle it without an exception
Merineth πŸ‡ΈπŸ‡ͺ
No i finished it. It works as intended. The next part is : "In the file FileDoesNotExistException.cs (in the folder Exceptions), there is a code skeleton for the class FileDoesNotExistException. Use this class to implement a user-defined exception that can be thrown if the text file in Chapter 2 doesn’t exist in the file system. The exception should contain an error message and the path to the non-existent file. Then modify the code in the class TextFileReader (Chapter 2), so that the exception is thrown if the text file doesn’t exist "
Pobiega
Pobiegaβ€’3mo ago
exceptions unique ability is to "bubble up" (move upwards) in the call stack okay so theres nothing there about catching the exception, might that be the next step?
Merineth πŸ‡ΈπŸ‡ͺ
Ah crap the exception should be thrown in the TextFileReader.cs and not in the menu
Pobiega
Pobiegaβ€’3mo ago
yeah that makes more sense πŸ˜„
Merineth πŸ‡ΈπŸ‡ͺ
Alright i see. So they want me to catch the exception here with my own user defined exception
No description
Merineth πŸ‡ΈπŸ‡ͺ
Damn i can't seem to find a solution. My program stops completely but in my teachers code which is nearly identical they get to try again
Pobiega
Pobiegaβ€’3mo ago
can you show me your latest code, both files?
Merineth πŸ‡ΈπŸ‡ͺ
namespace Workshop3.IO
{
public class TextFileReader
{
public static void StreamTextFile(string filepath)
{
// string str = File.ReadAllText(filepath);
// Console.WriteLine(str);

if (!File.Exists(filepath))
{
throw new FileDoesNotExistException(filepath);

}
// I create an object of StreamReader
StreamReader sr = new StreamReader(filepath);

// I read the first row of text. '?' indicates that the file might be empty.
string? line = sr.ReadLine();

// As long as the current line/row isn't empty.
while (line != null)
{
// Write the row of text onto the terminal.
Console.WriteLine(line);

// Read the next row of text.
line = sr.ReadLine();
}




}
}
}

namespace Workshop3.Exceptions
{
public class FileDoesNotExistException : Exception
{
public FileDoesNotExistException(string filepath) : base($"The user tried to input something that isn't valid. They tried {filepath} ")
{
}
}
}
namespace Workshop3.IO
{
public class TextFileReader
{
public static void StreamTextFile(string filepath)
{
// string str = File.ReadAllText(filepath);
// Console.WriteLine(str);

if (!File.Exists(filepath))
{
throw new FileDoesNotExistException(filepath);

}
// I create an object of StreamReader
StreamReader sr = new StreamReader(filepath);

// I read the first row of text. '?' indicates that the file might be empty.
string? line = sr.ReadLine();

// As long as the current line/row isn't empty.
while (line != null)
{
// Write the row of text onto the terminal.
Console.WriteLine(line);

// Read the next row of text.
line = sr.ReadLine();
}




}
}
}

namespace Workshop3.Exceptions
{
public class FileDoesNotExistException : Exception
{
public FileDoesNotExistException(string filepath) : base($"The user tried to input something that isn't valid. They tried {filepath} ")
{
}
}
}
Pobiega
Pobiegaβ€’3mo ago
That seems fine now show me your menu code
Merineth πŸ‡ΈπŸ‡ͺ
namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();


TextFileReader.StreamTextFile(filepath);
}
}
}
namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();


TextFileReader.StreamTextFile(filepath);
}
}
}
It stops the program at if (!File.Exists(filepath)) { throw new FileDoesNotExistException(filepath); }
Pobiega
Pobiegaβ€’3mo ago
yes your menu code does not use try/catch so it just crashes
Merineth πŸ‡ΈπŸ‡ͺ
I'll try and add that okay i think i did it!
try
{
TextFileReader.StreamTextFile(filepath);
}
catch (FileDoesNotExistException)
{
Console.WriteLine("Press Continue to try again..");
Console.ReadLine();
ReadTextFile();
}
try
{
TextFileReader.StreamTextFile(filepath);
}
catch (FileDoesNotExistException)
{
Console.WriteLine("Press Continue to try again..");
Console.ReadLine();
ReadTextFile();
}
I added this try and catch
Pobiega
Pobiegaβ€’3mo ago
almost! see, that ReadTextFile(); down there will actually cause issues
Merineth πŸ‡ΈπŸ‡ͺ
Oh i see. What happens after a catch has been found? Does it restart automatically?
Pobiega
Pobiegaβ€’3mo ago
calling a method from itself is called recursion no, it runs the code in the catch then nothing if you want to restart, you must manually do that let me show you
var person = new Person("John");

while (true)
{
try
{
Console.WriteLine("Enter an age for John: ");
var age = int.Parse(Console.ReadLine());
person.SetAge(age);
break; // exit the loop
}
catch (TooYoungException ex)
{
Console.WriteLine($"Age {ex.Age} is too young, please try again.");
}
}

Console.WriteLine("John is {person.age} years old.");

// program ends here

public class Person
{
public string Name { get; }

public Person(string name)
{
Name = name;
}

public void SetAge(int age)
{
if (age < 18)
{
throw new TooYoungException(age);
}
}
}

public class TooYoungException : Exception
{
public int Age { get; }

public TooYoungException(int age) : base($"Age {age} is too young.")
{
Age = age;
}
}
var person = new Person("John");

while (true)
{
try
{
Console.WriteLine("Enter an age for John: ");
var age = int.Parse(Console.ReadLine());
person.SetAge(age);
break; // exit the loop
}
catch (TooYoungException ex)
{
Console.WriteLine($"Age {ex.Age} is too young, please try again.");
}
}

Console.WriteLine("John is {person.age} years old.");

// program ends here

public class Person
{
public string Name { get; }

public Person(string name)
{
Name = name;
}

public void SetAge(int age)
{
if (age < 18)
{
throw new TooYoungException(age);
}
}
}

public class TooYoungException : Exception
{
public int Age { get; }

public TooYoungException(int age) : base($"Age {age} is too young.")
{
Age = age;
}
}
Merineth πŸ‡ΈπŸ‡ͺ
Oh interesting I manually added the recursion there such that the program doesn't just end if the user inputs a filepath that doesn't exist. Is recursion ok in c#? I'm very used to it in C
Pobiega
Pobiegaβ€’3mo ago
its generally avoided definately do not use if a normal loop does the trick its very useful for stuff like traversing a tree, or pathfinding etc because otherwise, imagine if I am a silly user and I keep entering a bad path it doesnt "restart" the method, it runs the method again, in a new stack frame
Pobiega
Pobiegaβ€’3mo ago
meaning...
No description
Merineth πŸ‡ΈπŸ‡ͺ
Oh yeah crap yeah i can def see an issue with that
Pobiega
Pobiegaβ€’3mo ago
every time you "restart" you actually open a new frame, further down the stack
Merineth πŸ‡ΈπŸ‡ͺ
Aahhh okay that makes sense now. I read through the code and with the Show() I can actually just view all the options again from the menu in the same Frame So i just simply got rid of the recursion
Pobiega
Pobiegaβ€’3mo ago
can you? show me
Merineth πŸ‡ΈπŸ‡ͺ
namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();

try
{
TextFileReader.StreamTextFile(filepath);
}
catch (FileDoesNotExistException)
{
Console.WriteLine("Press Continue to try again..");
Console.ReadLine();
}
Show();
}
}
}
namespace Workshop3.Menus
{
public class ReadTextFileMenu : Menu
{
public ReadTextFileMenu(string name)
: base(name)
{
Add(new MenuItem("Read Text File", new Command<ReadTextFileMenu>(this, r => r.ReadTextFile())));
}

public void ReadTextFile()
{
// Clear the console from previous text
Console.Clear();
// Asking the user for filepath
Console.WriteLine("Enter filepath..");
// Saving the filepath as a string object
string filepath = Console.ReadLine();

try
{
TextFileReader.StreamTextFile(filepath);
}
catch (FileDoesNotExistException)
{
Console.WriteLine("Press Continue to try again..");
Console.ReadLine();
}
Show();
}
}
}
Pobiega
Pobiegaβ€’3mo ago
Show comes from the base class, but I would guess it invokes the command action for the menu item somehow Im pretty sure you are creating another frame here too
Merineth πŸ‡ΈπŸ‡ͺ
RIp :catHeyHello:
Pobiega
Pobiegaβ€’3mo ago
read my age example with John again and you'll figure it out
Merineth πŸ‡ΈπŸ‡ͺ
That was my teachers solution xD Oki 1 sec
Pobiega
Pobiegaβ€’3mo ago
your teacher is a hack, we already knew this πŸ˜„
Merineth πŸ‡ΈπŸ‡ͺ
public class TooYoungException : Exception
{
public int Age { get; }

public TooYoungException(int age) : base($"Age {age} is too young.")
{
Age = age;
}
}
public class TooYoungException : Exception
{
public int Age { get; }

public TooYoungException(int age) : base($"Age {age} is too young.")
{
Age = age;
}
}
Quick question. What does the public int Age { get; } and Age = age; do?
Pobiega
Pobiegaβ€’3mo ago
the first declares a property. its an instance member variable that normally holds public information. in this case, its read only (no setter) Age = age sets the value of the Age property to the value given to the constructor I just wanted to show that the main benefit of making your own exceptions is that you can include actual data inside them not just a message
Merineth πŸ‡ΈπŸ‡ͺ
Ah yeah Hmm i'm not entirely sure i understand the first part. public TooYoungException(int age) I get that this is a method that accepts the argument int age public int Age { get; } this however hmm
Pobiega
Pobiegaβ€’3mo ago
well you know how you must declare a variable before you can use it?
Merineth πŸ‡ΈπŸ‡ͺ
It defines a read only property? Would this be a struct?
Pobiega
Pobiegaβ€’3mo ago
this is declaring a property, which is a form of instance member
Merineth πŸ‡ΈπŸ‡ͺ
Right yeah you got to declare a variable before you can use it In this case it's name is Age.
Pobiega
Pobiegaβ€’3mo ago
this is just like that, but instead of a local variable that only exists inside a method, this is an instance member meaning it will "keep" the value for as long as the instance lives so for this particular exception instance, it will have this Age value if I make another instance, it has its own Age
Merineth πŸ‡ΈπŸ‡ͺ
Ahhhh so the get that you use indicates that it's only a getter? i.e can't be changed only used?
Pobiega
Pobiegaβ€’3mo ago
correct!
Merineth πŸ‡ΈπŸ‡ͺ
Neat
Pobiega
Pobiegaβ€’3mo ago
you might have seen { get; set; } before
Merineth πŸ‡ΈπŸ‡ͺ
Yeah litearlly 5 minutes ago hahah, just read up about them
Pobiega
Pobiegaβ€’3mo ago
thats "both a getter, and a setter"
Merineth πŸ‡ΈπŸ‡ͺ
But yeah that makes a lot of sense
Pobiega
Pobiegaβ€’3mo ago
there is also { get; private set; } which means anyone can read, but only I can write
Merineth πŸ‡ΈπŸ‡ͺ
In my own mind i'd like to do public int age; But i'd assume this would be both a getter and setter?
Pobiega
Pobiegaβ€’3mo ago
that would be just a field a field doesnt have the concept of having getters or setters thats why we dont use them for public things
Merineth πŸ‡ΈπŸ‡ͺ
In C if i do int age; it would get a garbage value, deos this apply to c# also?
Pobiega
Pobiegaβ€’3mo ago
no you'd just get an uninitialized variable, which cant be used in C#
Merineth πŸ‡ΈπŸ‡ͺ
I see
Pobiega
Pobiegaβ€’3mo ago
C# has a runtime, so its not like C that just runs machine code so no "use after free" etc
Merineth πŸ‡ΈπŸ‡ͺ
I'm pretty sure C# uses machine code also?
Pobiega
Pobiegaβ€’3mo ago
not directly C# does not compile to native machine code
Merineth πŸ‡ΈπŸ‡ͺ
Ah it had something to do with .NET compiling iirc
Pobiega
Pobiegaβ€’3mo ago
yeah
Merineth πŸ‡ΈπŸ‡ͺ
public int Age { set; } Does this even have any use? Only being able to set a value without retrieving it sounds dumb
Pobiega
Pobiegaβ€’3mo ago
that has essentially no value, correct but regarding machine code, C# did quite recently get the ability to do "AOT compilation" this does result in machine code, but its... its complicated πŸ˜„
Merineth πŸ‡ΈπŸ‡ͺ
:catlaugh: I'm gonna continue with the program now. With writing this time... will open a poster later if something comes up thanks again (:
Want results from more Discord servers?
Add your server