Proposal: Boolean Literal Aliasing #2277
-
@jcdickinson commented on Wed Feb 27 2019 RationaleDisclaimer: I'm not sure if this is a good idea. All ideas are worth sharing. There is a thought school of developers who believe that booleans are evil. Once piece of code I previously worked with had some 10 boolean arguments, leading to code like this: wizard.ShowDialog(host, ..., true, false, false, false, false, true, false, true, true); A more relatable example for most is easy to find: new StreamWriter("C:\\tmp\\tmp.txt", true, Encoding.UTF8); What does that SslStream ssl;
ssl.AuthenticateAsServer(certificate, true, false); Opponents of booleans recommend either separate methods ( An existing way to deal with this problem would be to: new StreamWriter("C:\\tmp\\tmp.txt", append: true, encoding: Encoding.UTF8); The problem with that approach is that "all arguments following a named argument must be named." It adds friction to clarifying code in this way. We can't simply remove booleans from the framework. It would be nice to go over netcore and allow boolean call sites to document themselves. SolutionThe following constructor prototype (the syntax could use work): StreamWriter(string path, bool<Append, Clear> append, Encoding encoding) Is compiled to the following actual constructor prototype: StreamWriter(
string path,
[System.Runtime.CompilerServices.BooleanAlias("Append", "Clear")]
bool append,
Encoding encoding); It can then be used in one of the following ways: new StreamWriter("", bool.Append, null); // Equivalent/compiled to:
new StreamWriter("", true, null); // Is still valid in the face of boolean aliases
new StreamWriter("", bool.Clear, null); // Equivalent/compiled to:
new StreamWriter("", false, null); // Is still valid in the face of boolean aliases Treating
Other ConcernsWould the following be desirable? public bool<Found, NotFound> TryGetValue(TKey key, out TValue value);
if (dictionary.TryGetValue(key, out string value) == bool.Found) { } That smells of the @CyrusNajmabadi commented on Wed Feb 27 2019
This is pretty simply solved by just writing: new StreamWriter("C:\\tmp\\tmp.txt", append: true, Encoding.UTF8); The IDE even offers to do this for you if you use the quick-actions feature: @CyrusNajmabadi commented on Wed Feb 27 2019
it means this: ssl.AuthenticateAsServer(
certificate, clientCertificateRequired: true, checkCertificateRevocation: false); That seems pretty easy to understand. It's self documenting. And it's safe to someone even switching around the order of things. @CyrusNajmabadi commented on Wed Feb 27 2019
This is not true. See dotnet/roslyn#33753 (comment) as an example :) @CyrusNajmabadi commented on Wed Feb 27 2019
@gafter commented on Wed Feb 27 2019 Moving to |
Beta Was this translation helpful? Give feedback.
Replies: 18 comments
-
Well, these people are stupid. 🙃
It sounds more like this codebase simply wasn't making proper use of existing and much more viable alternatives. In this case, they should've realized creating a flags enum is much more efficient, and the official design guidelines state as much. There's no need to introduce some new arcane syntax for something that is easily doable with the simplest kind of type: an enum. Booleans are great to represent a 2-state choice, but when more choices are needed in your system, you should be flexible enough to redesign it, lest you keep on tightening bolts with a hammer. |
Beta Was this translation helpful? Give feedback.
-
The issue here is you're trying to use bools like they're enums. First off, if you used proper naming convention, this would already become more clear. Regardless, even if your enum has only two values ( Also, the code isn't that hard to write, nor space-taking:
Also, the overall syntax of Basically if you need more context and meaning behind bools, that's what enums are for. |
Beta Was this translation helpful? Give feedback.
-
Whilst that school of thought may well exist, I think you have perhaps confused it with the view (fact even) that boolean parameters are generally a bad idea. "Evil" is perhaps taking things a little far though. |
Beta Was this translation helpful? Give feedback.
-
As has been pointed out by @CyrusNajmabadi, this is no longer true. You might not be aware of that, since that change only happened in C# 7.2. |
Beta Was this translation helpful? Give feedback.
-
I'm not a huge fan of this idea and I agree with the opinion that using the named argument makes it clear enough. However, if I had to come up with a solution to this, I would propose the following: enum StreamWriterOptions : bool
{
Append = true,
Clear = false
} Both of these two invocations would compile: new StreamWriter("C:\\tmp\\tmp.txt", StreamWriterOptions.Append, Encoding.UTF8);
new StreamWriter("C:\\tmp\\tmp.txt", true, Encoding.UTF8); But the IDE would issue a warning on the latter and suggest a code fix. Anyway, I still don't think it's worth the effort. |
Beta Was this translation helpful? Give feedback.
-
I think that this would mainly just encourage bad code. If you have ten boolean arguments, you should probably be using a single Now, a feature to super easily unpack an int's bits into a bunch of booleans could be an interesting proposal, but that would belong in its own issue. |
Beta Was this translation helpful? Give feedback.
-
When #297 becomes a thing, you could just add a |
Beta Was this translation helpful? Give feedback.
-
Why wait? You can write extension |
Beta Was this translation helpful? Give feedback.
-
Oh yeah! Thanks for the reminder. |
Beta Was this translation helpful? Give feedback.
-
I'm certainly of the school of thought that boolean parameters are evil. I've even blogged about it more than once. So I'm really a fan of eliminating random true and false values that require investigation to understand, but I'm not too keen on the proposed syntax shown above. If you have control over the method declaration, it would be better to change the type to use an enum, as has already been suggested. For example, the original example could be rewritten by declaring an enum:
And then the constructor would be:
What would be nice would be a way to allow the name of the enumeration type to be omitted when calling the method ... so instead of needing to write ...
we could write
Perhaps this could be an attribute?
This would be a language change that doesn't change any of the syntax ... |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
There are some really well respected folks who hold this opinion, even if they weren't, they are still humans:
While this is true, that interface (Append/Clear) I provided comes directly from the BCL.
That probably renders this discussion pointless. @gafter Feel free to close this off. |
Beta Was this translation helpful? Give feedback.
-
Thanks @yaakov-h, I hadn't seen that one. Nice. |
Beta Was this translation helpful? Give feedback.
-
Any modern IDE gives you this automatically: |
Beta Was this translation helpful? Give feedback.
-
@dsaf not everyone works with code in an IDE. Code review is often done in a different tool. On the other hand, you can add StyleCop that auto-rejects naked boolean literals in such cases. |
Beta Was this translation helpful? Give feedback.
-
I think the crux of this suggestion is akin to declaring inline enums, which itself is not an unreasonable request. It can be overly verbose to declare a whole separate enum (in a whole separate file) just to be able to accurately specify the arguments to a single method. TypeScript has a concept of string-literal types, which can function a lot like an inline enum. It allows your method signature to declare the complete set of acceptable values for a given parameter. |
Beta Was this translation helpful? Give feedback.
-
@dsaf, |
Beta Was this translation helpful? Give feedback.
-
@DavidArno yeah, it's not perfect. Still is it that worse than VS code lens thing? Eventually I might just go back to only using https://github.com/MrJul/ReSharper.EnhancedTooltip . |
Beta Was this translation helpful? Give feedback.
@jcdickinson
As has been pointed out by @CyrusNajmabadi, this is no longer true. You might not be aware of that, since that change only happened in C# 7.2.