Reputation: 92805
I have the following method I'm working on:
private IEnumerable<TreeNode> GetChildNodes(TreeNode parent)
{
string path = parent.Tag.ToString();
// Add Directories
string[] subdirs = Directory.GetDirectories(path);
foreach (string subdir in subdirs)
{
yield return GetChildNode(subdir);
}
// Add Files
string[] files = Directory.GetFiles(path);
foreach (string file in files)
{
var child = GetChildNode(file);
fileNodeMap[file] = child;
yield return child;
}
}
This works fine with the exception of Directory.GetDirectories() and Directory.GetFiles() can both throw exceptions that I want to catch.
I can't catch the pieces of code which utilize those methods due to my use of yield (yields can't be placed within the body of a try if there is a catch). I know I could remove the yield and simply add to my children to a collection but I'm curious how someone would catch IOExceptions from both of those methods and still utilize yield?
Upvotes: 1
Views: 1611
Reputation: 111870
The exception will be throw by the call of GetDirectories
and GetFiles
, so you can try-catch
THEM instead of the for-each
,
Upvotes: 0
Reputation: 7484
I would caution against using exceptions as a method of control flow - if you aren't sure that the directory or path is going to return a valid result, check for it first - almost all of those exceptions can be prevented by argument checking, something generally like the below.
private IEnumerable<TreeNode> GetChildNodes(TreeNode parent)
{
string path = parent.Tag.ToString();
if (String.IsNullOrEmpty (path) || String.IsNullOrWhiteSpace (path))
yield break;
// I'm not aware of a constant/enum for the maximum allowed path length here :(
if (path.Length > 260 || path.Any (Path.GetInvalidPathChars ().Contains))
yield break;
if (!Directory.Exists (path))
yield break;
Func<string[], Func<string[],string>,> SafeIO = (fn, arg) => {
try {
return fn (p);
} catch (IOException) {
return new string[0];
}
};
// Add Directories
string[] subdirs = SafeIO (Directory.GetDirectories, path);
foreach (string subdir in subdirs)
yield return GetChildNode(subdir);
// Add Files
string[] files = SafeIO (Directory.GetFiles, path);
foreach (string file in files) {
var child = GetChildNode(file);
fileNodeMap[file] = child;
yield return child;
}
}
Plenty of room for optimization there (and ripe for further decomposition), and the usual comments apply about race conditions and the lack of a guarantee for checking if a directory exists before it is deleted on another thread, so now you can make this more robust by wrapping a try/catch around the Get{Directories,Files} calls like Jon or xanatos suggested (EDIT: and that I've now wrapped up here as SafeIO
) - but now you can catch only the specific exception that is susceptible to this (IOException
or DirectoryNotFoundException
) and reserve it for truly exceptional cases.
Upvotes: 0
Reputation: 22149
You could make helper methods that add your special error handling sauce:
private string[] GetSubdirectoriesWithSpecialSauce(string path)
{
string[] subdirectories;
try
{
subdirectories = Directory.GetDirectories(path);
}
catch (IOException ioe)
{
ShutdownWOPR();
CallDrFalken();
}
return subdirectories;
}
And obviously substitute the relevant calls. I of course assumed you wanted to yield even on errors, but I humbly accept that this assumption may be wrong :)
Upvotes: 0
Reputation: 4728
Could you not catch exceptions outside, in the code that calls them?
Upvotes: 0
Reputation: 1500873
How about something like (for the first part):
string[] subdirs;
try
{
subdirs = Directory.GetDirectories(path);
}
catch (IOException e)
{
// Do whatever you need here
subdirs = new string[0];
}
And similarly for the second. You don't need to yield within that try block. If this doesn't help, please write whatever code you would want to be valid, so that we can see what you're planning to do if an exception is thrown.
Upvotes: 5