Replies: 14 comments
-
You could wrap the resource in a |
Beta Was this translation helpful? Give feedback.
-
It's not about this specific problem but about a further development of the try-finally-catch philosophy. |
Beta Was this translation helpful? Give feedback.
-
The problem I see here is that by far the most common use case for try
{
using (var sw = new StreamWriter(pathToFile))
{
/* sw will be closed */
}
}
catch (IOException ioex)
{
Logger.Log(ioex);
}
catch (UnauthorizedAccessException uaex)
{
// TODO: Tell user, that file is unaccessable
}
finally
{
// this is a common traditional finally, called always at the very end
// (though it's unlikely to be here in reality
} For this to be a compelling new feature therefore, it needs a good use-case that |
Beta Was this translation helpful? Give feedback.
-
@lachbaer The dispose pattern is introduced for doing clean-up too, and I don't think anything is necessary to be put in |
Beta Was this translation helpful? Give feedback.
-
I find this very confusing. Excluding to speak about the bad example (use a using or dispose the StreamWriter in the finally), an implement like this can create a try with 4 or 5 catch and 5 or 6 finally, it's just hard to understand and to maintain. And I don't find what stuff you can do in a exception's finally that you can't/have to do in this exception's catch. And I don't see the point to handle a same exception in several code blocks. |
Beta Was this translation helpful? Give feedback.
-
PS: I written about the example that is a bad one because of this : To me this two decisions are bad design decisions, and bad decisions have to be fix by the developer and not by the framework. I understand that this issue is not about this specific example but we need to provide some good ones to prove that this multiple finally blocks could be usefull in some situations, and they are not just catch blocks duplications, and this example isn't speak in favour of this proposal. |
Beta Was this translation helpful? Give feedback.
-
I think this use-case is actually useful. Its quite often I need to have special handling for different kinds of exceptions and than common (logging) code for all of them. However, you can achieve it today by putting common code in exception filter. The semantics are a bit different - common code executes before, not after, specific code - but in most cases it does not matter. StreamWriter sw = null;
try
{
sw = new StreamWriter(pathToFile);
{
/* ... */
// no exception occured up to here,
// so we can be sure that the stream exists and is open
sw.Close();
}
}
catch (Exception ex) when (((Func<bool>)(() =>
{
// this is executed BEFORE any catch but only if matching exception is thrown
if (sw != null)
sw.Close();
return false; //return false is essential so other catch clauses are tried
})).Invoke()) { }
catch (IOException ioex)
{
Logger.Log(ioex);
}
catch (UnauthorizedAccessException uaex)
{
// TODO: Tell user, that file is unaccessable
}
finally
{
// this is a common traditional finally, called always at the very end
}
//special handling if file is malformed
}
finally
{
//dispose of streams
} |
Beta Was this translation helpful? Give feedback.
-
@zippec, catch (Exception ex) when (((Func<bool>)(() => That is one awesome "hack". I so wish I'd thought of that! 😀 |
Beta Was this translation helpful? Give feedback.
-
Interesting that CLR already supports fault clauses. |
Beta Was this translation helpful? Give feedback.
-
One possible use case I can see for a feature like this would be in factory methods that return IDisposable types, or constructors that assign IDisposable fields. These require that the object not be disposed in the case of no exceptions thrown. This case is not handled by public DisposableType MakeDisposable()
{
var foo = new DisposableType();
try
{
// Do some stuff with foo that may throw exceptions
return foo; // We don't want this to be disposed
}
fault
{
// Exception occurred, bail out!
// Consumer won't have access to foo, so it's our responsibility to dispose it
foo.Dispose();
}
} Currently this can be emulated to a certain extent by catch-and-rethrow, but this affects the stack trace and requires duplication if certain exception types get special-cased. |
Beta Was this translation helpful? Give feedback.
-
@ajtribick Workaround I use: public DisposableType MakeDisposable()
{
var foo = new DisposableType();
var success = false;
try
{
// Do some stuff with foo that may throw exceptions
success = true;
return foo; // We don't want this to be disposed
}
finally
{
if (!success)
{
// Exception occurred, bail out!
// Consumer won't have access to foo, so it's our responsibility to dispose it
foo.Dispose();
}
}
} |
Beta Was this translation helpful? Give feedback.
-
@jnm2 - I don't particularly like the success variable approach because it is possible to forget a |
Beta Was this translation helpful? Give feedback.
-
@ajtribick It's not my absolute favorite but it does the job. If I remember right, a well-timed ThreadAbortException can have the same effect if it happens once this method returns, so it's really no safer: using (var foo = AllocateFoo())
{
// Use foo
} Compiles to the equivalent of: var foo = AllocateFoo();
// Oops, ThreadAbortException.
try
{
// Use foo
}
finally
{
foo?.Dispose();
} Plus .NET Core has explicitly made thread abort hardening a non-goal. |
Beta Was this translation helpful? Give feedback.
-
Plus, even this is susceptible IIRC: Foo foo;
try
{
foo = /* Oops, ThreadAbortException after the return but before the stloc */ AllocateFoo();
// Use foo
}
finally
{
foo?.Dispose();
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
This is an extension that adds "exception based finally conditions".
Maybe a CLR change is needed.
Idea
The primary idea is to reduce code duplication in multiple occuring catch blocks, with respect to the corresponding exceptions.
This extends the common try-catch pattern, known by many other languages, and could serve as a role model for other languages as well.
Add a
finally
to thetry-catch
construct that is only called in case of an occuring (matching) exception.(Btw, this example is only made up! ;-)
Beta Was this translation helpful? Give feedback.
All reactions