-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Proposal: Guard statement in C# #511
Comments
The proposal above says the guard expression would be a public void Foo(object o)
{
guard (o is Bar b) else throw new ArgumentException(nameof(o));
// do something with `b`...
} |
You can already write public void Foo(object o)
{
if (o is Bar b) {} else throw new ArgumentException(nameof(o));
// do something with `b`...
} Not sure how adding a new keyword and statement form makes this any better. |
using a public void Foo(object o)
{
if (!(o is Bar b)) throw new ArgumentException(nameof(o));
// do something with `b`...
} having a public void Foo(object o)
{
Contract.Requires(o is Bar b, else: () => throw new ArgumentException(nameof(o)));
// do something with `b`...
} and even cleaner with #119 / #105: public void Foo(object o)
requires o is Bar b else throw new ArgumentException(nameof(o)))
{
// do something with `b`...
} I doubt that there is much use for a |
The proposal without the comments was already imported in #138 |
OK, closing as a dup of #138. |
Seems like it would make more since to just create a static Guard class like so... `
} Then you can use it like so...
You don't even have to use the return value of course. The issue I guess is deciding if you want to throw an exception or do some other kind of error handling. If your pattern is changing that much, then what's the point of something baked into the language? |
@FreeRadical7 This issue was closed long ago. |
@Eyas commented on Tue Jan 26 2016
Background
Swift has a guard statement that can be very valuable in writing code that is easy to reason about. See Swift Guard Statement - why you should use it.
Problem
If we have a function
Foo
that takes an integer that must be positive (> 0
), we can write that as:Proposed Solution:
guard
statementsBenefits
The guard statement introduces two benefits over
if
-based guards:Grammar
@alrz commented on Fri May 20 2016
Also check out #119 and #6400.
@Eyas commented on Tue Jan 26 2016
@alrz right.
unless
is what its called in Ruby, Swift is calling itguard
. I preferguard
because it relates to the guard refactoring pattern.While I like contract I think this is quite different from #119: the
guard
block could result in an exception thrown for an invalid argument, but could also include non-errored handling of edge cases. E.g.:The
else
clause in #6400 is very close to what we want, but:boolean-expression
rather thanpattern
.@mburbea commented on Tue Jan 26 2016
And what's wrong with
It handles your case. I don't find that
unless
orguard
Adds a lot of value to the language. It adds another way to do the exact same thing, and then developers will have to learn new syntax, and have another form to worry about when reading code.@Eyas commented on Tue Jan 26 2016
@mburbea This is what I explained in the "Problem" section, the main issue with the
if (!(...)) {
is that the compiler doesn't guarantee that the embedded statement after theif
-guard terminates. So I can do something like:So yes, benefits are:
guard (b > 0) else ...
) is "pretty" syntactic sugarelse
clause will terminate, so that code after theguard
clause provably runs only when theguard
conditions are true.@HaloFour commented on Tue Jan 26 2016
@Eyas
#6400 does handle that,
else
mustthrow
orreturn
.Otherwise I don't see anything particularly wrong with it but I'm not sure that it's particularly necessary. I want to dislike it more just because Swift has it, but meh.
@HaloFour commented on Tue Jan 26 2016
I will say that the one reason that I dislike this and #119 is that it doesn't offer any proposal for metadata attached to the method to declare such constraints. Handling illegal arguments is one thing, preventing them from happening is significantly more.
@Eyas commented on Tue Jan 26 2016
@HaloFour See my examples in the comment above:
guard
clause does not constrain parameters or variables passed, but rather provides a guarantee on these variables after the statement. Theelse
clause can simply return (seefib
example). This is valuable as-is, and is orthogonal to annotating the "type" of a function or scope.guard
clause is different thanlet
in that:and
of multiple values, or a more complex condition, rather than a condition related to a single variable or assignment. See theConfigure
example.@HaloFour commented on Tue Jan 26 2016
@Eyas You're right. I think after the
defer
proposal I was looking for a fight. 😉@Richiban commented on Mon Feb 08 2016
I still think that the requirements this proposal seeks to address would be supported by pattern matching -- I think it would even look fairly similar. Consider this pseudocode:
You get all the requested features above:
@DerpMcDerp commented on Wed May 18 2016
The big problem with guard statements (and pattern matching in general) is that the boolean test isn't able to be abstractable out e.g.
Forcing you to repeat the
guard (b > 0) else throw blah
line everywhere. It would be better if C# had an erasable psuedo-type that combines a type and a predicate in a single place once and for all, e.g. if something like this:was syntax sugar for:
@HaloFour commented on Wed May 18 2016
@DerpMcDerp
I don't know what that has to do with
guard
per se, nor whatguard
has to do with pattern matching aside the potential for patterns to be used in arbitrary conditional expressions. If you want the reusability you can just call a helper function:That seems significantly cleaner than trying to hack something into the type system.
As for reusability in pattern matching, that sounds like it would be active patterns.
@DerpMcDerp commented on Wed May 18 2016
There are 2 annoyances with the helper function approach that make it less cleaner than hacking it into the type system for common places in code:
@HaloFour commented on Wed May 18 2016
@DerpMcDerp It's less code, but that doesn't make it cleaner. It hides much of what it does through syntactic voodoo. It blends the concerns of normal conditions and validation. It hijacks the type system in a completely unprecedented way and changes the public contract of methods using them. As "erased" it can't be reused, requiring all of these common "guards" to be constantly reimplemented over and over again.
If we're going to go down the path of declarative validation I'd rather explore AOP options through code generators. That or #119.
@gafter commented on Wed May 25 2016
What
guard
has to do with pattern-matching is that pattern variables introduced in the expression of aguard
statement would be in scope in the enclosing block. See #11562The text was updated successfully, but these errors were encountered: