Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicit Try Feature Request #13367

Closed
FuncLun opened this issue Aug 25, 2016 · 13 comments
Closed

Implicit Try Feature Request #13367

FuncLun opened this issue Aug 25, 2016 · 13 comments

Comments

@FuncLun
Copy link

FuncLun commented Aug 25, 2016

A great feature to add to C# would be an implicit try around (almost) any { } code block. This would only be syntax sugar, so it should be backwardly compatible on older frameworks.

This feature would reduce the noise and nesting where catching errors is needed. It would make it easier to add error handling, therefore more likely for a developer to add.

In this code example, I've commented out the code that the compiler would implicitly add.

public class Widget
{
    public string Name
    {
        get
        {
        //try {
            return ("DoWork");
        }
        catch (Exception exception)
        {
            Debug.WriteLine(exception.Message);
            return null; //Must throw or return
        }
        //}
        set
        {
        //try {
            Debug.WriteLine("DoWork");
        }
        catch (Exception exception)
        {
            Debug.WriteLine(exception.Message);
        }
        //}
    }

    public bool DoSomething()
    {
    //try {
        if (true)
        {
        //try {
            Debug.WriteLine("DoWork");
            return true;
        }
        catch (Exception exception)
        {
            Debug.WriteLine(exception.Message);
        }
        //}
    }
    catch (Exception `exception)`
    {
        Debug.WriteLine(exception.Message);
        throw; //Must throw or return
    }
    //}
}
@keichinger
Copy link

A great feature to add to C# would be an implicit try around (almost) any { } code block. This would only be syntax sugar, so it should be backwardly compatible on older frameworks.

To me this doesn't sound like it's backwards compatible since some frameworks and apps may rely on an exception bubbling the call chain upwards to deal with them in some other place.

So many try-catch blocks don't make a lot of sense to me to be honest and sound more like a code smell. If your application/library throws an exception (keep in mind, they're usually thrown for the cases that you don't know how to handle - something exceptional) and you immediately swallow it, you application might go into a state that could break even more stuff and do some serious damage.

@jaypeeZero
Copy link

@cH40z-Lord I don't see how this suggestion would stop exception bubbling from being possible.

You might be getting lost in the details of the code he used.

How about these examples:

if (someVariable == true) {
    AFunction();
} catch (SomeException ex) {
    DoSomething();
}

using (var thing = new Thing()) {
    ImportantProcess();
} catch (BadThing ex) {
    FixThings();
}

@FuncLun
Copy link
Author

FuncLun commented Aug 25, 2016

@cH40z-Lord This feature would be backward compatible, just as the property "Lambdas for Getter-Only Auto-Properties" is backward compatible - requires new compiler, but works with framework 2.0. As for apps relying on exception bubbling - this feature does not affect design patterns. It just allows a try-catch to be added more easily and without unnecessary code and nesting.

My code sample was to demonstrate where the requested feature could be used. This is not a recommendation that every property, function or if block have a catch statement. Swallowing exceptions is a bad idea and would smell terribly. However, the catch statements could be used to provide additional logging and then "throw;". For example, an MVC error handler could easily catch an exception thrown 12 layers deep, but the stack trace isn't going to be very helpful on a function that runs properly 99 out of 100 times. You might know where, but not why or how to reproduce. A function that threw could easily log the input variables to make it much easier to reproduce and fix. After your exception throws, you could have 12 levels of logs explaining what was going on when an exception was thrown.

using (var thing = new Thing()) is a great example. First of all, using() is just sugar syntax around a try {} finally {} statement. using() is bad for any objects that might throw in the Dispose() method (namely, wcf objects https://msdn.microsoft.com/en-us/library/aa355056(v=vs.110).aspx). You'll catch the dispose exception instead of the first exception. This feature's catch{} should be before the finally{} of a using() statement.

And, thank you for your feedback.

@huoyaoyuan
Copy link
Member

At least implicit try for using block is reasonable.

using (var thing = new Thing()){...}
catch {...}

should be expanded as

try {var thing = new Thing(); ...}
catch {...}
finally {thing?.Dispose();}

But there is one problem that for "simple" nested using, like

using (var thing1 = new Thing())
using (var thing2 = new OtherThing())
{

}

Notice that you can't combine them because thing1 and thing2 are different types.
Should the catch catch all the constructions, or only the inner one?
In fact, I'm not sure how the compiler deals with simple nested using. I think it's reasonable to generate only one try block.

@FuncLun
Copy link
Author

FuncLun commented Aug 25, 2016

Good point on the double using statements. That does create multiple/nested try-finally blocks, With your example, OtherThing is disposed first, then Thing. If OtherThing?.Dispose(); throws an exception, Thing?.Dispose(); is still called. The last exception thrown is the one that bubbles up.

The using expansion is more like this:

var thing = new Thing();
try {...}
catch {...}
finally {thing?.Dispose();}

because thing is available in the 'finally' scope, and you can't declare var thing before/after the using statement. Also, if the constructor throws, there is no call to Dispose().

The implicit try-catch would be to catch an exception inside the try {...} block. The constructors are before {, and disposal after }, so exceptions there would not be caught. With multiple usings, thing1 and thing2 would be available in the catch.

@HaloFour
Copy link

I don't like the idea of having a block of code implicitly within a try block just because you have a catch block after the scope. That try keyword and the scope it introduces provides a hint that the code in question has the potential to throw and I think that is important to explicitly state.

For using specifically it's not particularly clear in which order the operations happen. For example, the Java equivalent would be:

try (AutoCloseable resource = getResource()) {
    // do stuff here
}
catch (Throwable exception) {
    // handle
}

Which expands out to:

try {
    AutoCloseable resource = null;
    try {
        resource = getResource();
        // do stuff
    }
    finally {
        if (resource != null) resource.close();
    }
}
catch (Throwable exception) {
    // handle
}

You might not expect the resource to be cleaned up prior to the exception being caught, or that the handler might catch exceptions thrown by AutoCloseable.close().

I could see relaxing the requirements around try so that it doesn't require to be followed by a block but instead can be followed by a statement. That way the following should be legal:

try if (condition) {
    // do stuff
}
catch (Exception exception) {
    // handle this
}

@aluanhaddad
Copy link

As others have noted, I think the problem with this is that it encourages people to catch more exceptions. Because less syntax is required, it becomes easier to justify a catch and log idiom (where people may forget to re-throw). Another problem is that the scope of the implicit try will often be both broader than it needs to be, and we will lose the explicitness of which specific lines we expect an exception from as @HaloFour pointed out.

@Thaina
Copy link

Thaina commented Oct 1, 2016

I like syntax try XXX

But if it not support the catch syntax of this post. I feel it would be ambiguous for the loop

Consider this

try while(true) { }
catch() { }

Is it

try
{
    while(true) { }
}
catch() { }

or

while(true)
{
    try { }
    catch() { }
}

I wish we could have both. And it would work like this

try while(true) { }
catch() {}
// is
try
{
    while(true) { }
}
catch() {}

and

while(true) { }
catch() {}
// is
while(true)
{
    try { }
    catch() {}
}

@aluanhaddad
Copy link

Sorry to 🚲🏠 but perhaps postfixing catch to an expression or placing it just prior to the semicolon of a statement would make this unambiguous. That said, @HaloFour's solution is also good.

@Logerfo
Copy link
Contributor

Logerfo commented Mar 2, 2017

I like this one. Also, why does try/catch/finally scopes need to be explicit, unlike other expressions?

@aluanhaddad
Copy link

@Logerfo maybe because we should all use braces 😛

@alrz
Copy link
Member

alrz commented Mar 6, 2017

@Logerfo #16093 (comment)

@CyrusNajmabadi
Copy link
Member

Closing this out. We're doing all language design now at dotnet/csharplang. If you're still interested in this idea let us know and we can migrate this over to a discussion in that repo. Thanks!

@CyrusNajmabadi CyrusNajmabadi closed this as not planned Won't fix, can't repro, duplicate, stale Nov 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests