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

Try-Catch Operator #3666

Closed
osamakawish opened this issue Jul 11, 2020 · 13 comments
Closed

Try-Catch Operator #3666

osamakawish opened this issue Jul 11, 2020 · 13 comments

Comments

@osamakawish
Copy link

osamakawish commented Jul 11, 2020

It's been mentioned on StackOverflow, but I'll suggest it here. I think this would sort of nullify the need for null-coalescing operators to a degree, and would generalize it.

But basically, it would be nice to have a generic simplification of try-catch by simplifying:

try { 
    A()
}
catch {
    B()
}

to:

A() !? B()

This is just an example. I'm using the symbol for the null-coalescing operator (??) since this generalizes the concept. We can just as easily use something else in place of ??.

Examples:

Null

The obvious starter being the null case. It'll behave similarly to the null coalescing operator here.

int? a;
int b = a !? -1;

Listed Dictionary Insertion

Dictionary<string, List<int>> dict;
dict["a"].Add(5) !? (dict["a"] = new List<int> {5});

Division By Zero

int a; int b;
int? c = (a / b) !? null;

Notes

  • A possible generalization could include a finally statement. Though I personally haven't used them much to feel comfortable providing coherent examples.
  • Using the ?? here wouldn't cause issues with pre-existing code, since we're only generalizing the ?? operator.

Edit. Substituted the ?? with !?

@osamakawish osamakawish changed the title Proposal: Try-Catch Operator Try-Catch Operator Jul 11, 2020
@HaloFour
Copy link
Contributor

Using the ?? here wouldn't cause issues with pre-existing code

It certainly would, it would change the semantics of all of that code by silently adding an exception handler. Expected exceptions would suddenly never be thrown, just swallowed. That is a serious breaking change and would never be considered.

@osamakawish
Copy link
Author

osamakawish commented Jul 11, 2020

@HaloFour Fair point. Either way, we could still use different notation to simplify it. It wasn't really the crux of the idea anyways tbh. I just suggested it given that it would make things simpler. (Also didn't consider the subtle consequence you mentioned.)

Edit. Out of curiosity, was that the only reason for the thumbs down lol... because that was more of subtle mistake than a criticism of the entire proposal...

@svick
Copy link
Contributor

svick commented Jul 11, 2020

My main issue with this proposal is that it promotes a general catch that catches any exception, even those that you didn't mean to catch, which is generally considered to be a bad practice.

Also, similar proposals have been made before, among them dotnet/roslyn#16072, #786 and #464.

@osamakawish
Copy link
Author

osamakawish commented Jul 11, 2020

@svick This is mostly meant to be convenient for simple cases like in the examples provided, but we could just as easily generalize it:

int? c = (a / b) !? null !! DivideByZeroException;

or:

int? c = (a / b) !DivideByZeroException? null;

(Though the latter example may be confusing to implement)

Although at this point, I think the syntax is starting to get a little complicated. Still, it's fairly readable once the dev using it develops an intuition behind it. It's definitely cleaner than a full try-catch statement.

Edit. Also, I agree that it's generally bad practice. But again, for simple situations, it's not a significant issue, when it's fairly obvious what the exception will likely be. But granted, that becomes a slippery slope for more regular usage of the general catch. But imo, if the general catch is part of the language, I don't get the issue with simplifying it. Either that, or the general catch should at least raise a compiler warning imo.

@foxesknow
Copy link

foxesknow commented Jul 11, 2020

int? a;
int b = a !? -1;

Erm, why not just write

int b = a ?? -1

or even:

int b = a.GetValueOfDefault(-1)

And for:

int a; int b;
int? c = (a / b) !? null;

what's wrong with:

int? c = b switch
{
    0 => null,
    _ => a / b
};

I'm not sure that making exceptions part of the "normal" flow of the language is a good thing. They are, by their very nature, exception, and what you're proposing promotes them more to the level of regular flow control, which I don't think is a good thing.

@osamakawish
Copy link
Author

@foxesknow

int? a;
int b = a !? -1;

Erm, why not just write

int b = a ?? -1

Those two are essentially the same thing. The int? a was just preliminary code. (I forgot to include = null, but I tend to assume that objects initiate by default as they do in C++, given my C++ background).

They are, by their very nature, exception, and what you're proposing promotes them more to the level of regular flow control, which I don't think is a good thing.

I'm not sure how I'm promoting them to regular flow control at all. I'm simply suggesting that simple cases of exception handling be dealt with cleaner code. That is it. That's the only essence of this proposal.

Your examples seem nice, but try something similar with the listed dictionary example. You may be able to do it, but the point is, the try-catch one-line notation just does it more cleanly.

@jnm2
Copy link
Contributor

jnm2 commented Jul 11, 2020

Relevant: #220, #464, #786, dotnet/roslyn#16072, https://github.com/dotnet/roslyn/issues/16078.

My opinion on it, like many others’, has always been negative: dotnet/roslyn#16072 (comment)

@HaloFour
Copy link
Contributor

HaloFour commented Jul 11, 2020

but try something similar with the listed dictionary example

var list = dict.GetOrAdd("5", _ => new List<int> { 5 });

There are many alternate approaches to using exception handling in these cases which are virtually universally better. A syntax that intentionally ignores exceptions would be a pit towards bad practices.

@Unknown6656
Copy link
Contributor

What's speaking against a Framework method a la

public static void TryDo(Action? func)
{
    try
    {
        if (func is { } f)
            f();
    }
    catch
    {
    }
}

public static T TryDo<T>(Func<T> func, T default_value)
{
    try
    {
        if (func is { } f)
            return f();
    }
    catch
    {
    }

    return default_value;
}

which one can use as follows:

int a; int b;
int? c = TryDo(() => a / b, null);

I think that TryDo is more readable than !?. I also think that exceptions should be avoided at all costs. The code above should be written as:

int a; int b;
int? c = b != 0 ? a / b : null;

@osamakawish
Copy link
Author

osamakawish commented Jul 11, 2020

I'm not really sure it's bad practice to entirely avoid exceptions.

Regardless of everyone's personal opinions on exceptions, especially catch-all exceptions, they are an inherent component of C#.

Again, the least that could be done here is a compilar warning for catch-all statements. But we don't have that (unless I'm using an earlier version).

It does seem people here seem to resent exceptions, though I'm not sure why. Just because they can be avoided isn't exactly an argument for why they should be. They are perfectly legal code. I take back this point. Legal code or not, I get that exceptions are generally "ugly".

@HaloFour
Copy link
Contributor

@osamakawish

The bad practice is in swallowing the exceptions. There are scenarios where it is warranted, but they are the exception (pun intended) and I think having to write a little extra syntax in those cases is not only far from onerous but I think it would remind the developer to think about why they're doing it.

@osamakawish
Copy link
Author

Fair points. I'm going to close this here then.

I suppose exceptions remain useful for when alternatives haven't been implemented, or for devs with an incomplete understanding of a class or C# in general.

@jnm2
Copy link
Contributor

jnm2 commented Jul 11, 2020

That's a good way to say it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants