𝄯 Sharp Regrets: Top 10 Worst C# Features
-
should we really design programming languages to cater idiots?
There are people here (OK, just one really) who believe that everyone who wants to should be able to program.
-
should we really design programming languages to cater idiots?
No, but we should design them to be as simple as possible.
-
There are people here (OK, just one really) who believe that everyone who wants to should be able to program.
Well, why shouldn't they?
-
Are we still talking about the idiots or people in general?
-
This is what you wrote:
There are people here (OK, just one really) who believe that everyone who wants to should be able to program.
This is what I am replying to:
There are people here (OK, just one really) who believe that everyone who wants to should be able to program.
OH HEY THEY ARE THE SAME!
-
I keep forgetting that you're context-impaired. You are aware that "everyone" includes "idiots", right?
-
You are aware that "everyone" includes "idiots", right?
Yes, I post on this forum, don't I?
-
At large, I agree with your first paragraph, but properties have been there in VBA as long as I know that, and I suppose this is something C# has taken from the BASIC side.
VBA also has a switch statement (calledselect
) that breaks after the first case it executes completes.Although in this case, I think that properties can be really useful, like the background color of a control element which needs some coding to visually reflect the change of the property.
Personally, I'd like if the repainting didn't happen immediately, but it was delayed until next main loop cycle. I know it's exactly what happens in real life (assuming non-idiotic implementation), but it was just an example to illustrate the drastic semantics change in accessing variables.No, but we should design them to be as simple as possible.
Everyone has different idea what it means for something to be simple. Easy to use? Non-complex? How to measure complexity? What it means to be easy to use? And where lies the boundary of "as possible"?Well, why shouldn't they?
For the same reason why people who can't tell throttle from brake aren't allowed to drive.
-
Everyone has different idea what it means for something to be simple.
That's true, and it's a huge problem. Ruby of Rails or WebAPI guys think "simple" means the framework does a million of things behind the scene so you write fewer lines of code. I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.
Obviously I'm right here.
For the same reason why people who can't tell throttle from brake aren't allowed to drive.
Yes they are. At least, here in the US.
-
Obviously I'm right here.
Right where? I don't see you anywhere.
I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.
I both agree and disagree with you. But this is like arguing about which license is more free without having a precise definition of the word free.
-
I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.
s/that/properties/ and it could be what I might've said. Except I've got so good at debug-print-debugging at my current company I doubt I would find myself complaining about this.Obviously I'm right here.
While obvious is kind of subjective, thought it's impossible to disagree on the definition of "right".Yes they are. At least, here in the US.
I'm dubious of any facts coming from blakeymouth. Can anyone else confirm that?
-
But this is like arguing about which license is more free without having a precise definition of the word free.
Still, imprecise definition isn't as bad as outright wrong one, like that one made up by FSF.
-
VBA also has a switch statement (called select) that breaks after the first case it executes completes.
Select has so many features that switch doesn't that they can barely be called similar.
@Gaska said:Personally, I'd like if the repainting didn't happen immediately, but it was delayed until next main loop cycle.
It does. Windows has an API call named Invalidate that tells the OS that a region of a window is dirty and Paint should be called on it when it next comes into view. Anyone not using it (or not using something that does it properly for you) is Doing It Wrong©.
-
Ruby of Rails or WebAPI guys think "simple" means the framework does a million of things behind the scene so you write fewer lines of code. I see that as "complicated" because it involves trusting in blackbox behavior you can't verify and the instant something goes wrong, you're completely lost determining how to debug or fix it.
Thesis: "simple" is not the same as "easy" and in the long run, it's better to do things the simplest way rather than what's easiest right now.
Not really news when you put it that way.
-
I think I'd prefer the phrase "straightforward" over "simple".
I want code that's straightforward. It might be complicated as shit, but if it's straightforward, at least I can plug in a debugger and trace through it and figure it out.
-
Still, imprecise definition isn't as bad as outright wrong one, like that one made up by FSF.
-
Just wait.
-
Select has so many features that switch doesn't that they can barely be called similar.
A quick read-up on a construct I've never seen in a language I've never used left me thinking that all those "so many features" boil down to ranges. Which in reality isn't that much (kinda like std::shared_ptr isn't a big addition to C++).I think I'd prefer the phrase "straightforward" over "simple".
"Straightforward" has the same problem as "simple" - people can never agree what is the most straightforward thing because everyone has different preferences.
-
A quick read-up on a construct I've never seen in a language I've never used left me thinking that all those "so many features" boil down to ranges.
Select Case True Case x = y ' Do stuff Case a = b ' Do other stuff Case Else ' More stuff End Select
C's switch is restrictive enough that the compiler has a lot of capabilities for optimization. VB's Select Case allows arbitrary expressions in Case, which hinders optimization, but adds a ton of flexibility. It also has several range syntaxes as you mentioned.
-
"Straightforward" has the same problem as "simple" - people can never agree what is the most straightforward thing because everyone has different preferences.
Well then I'll just kick them in the crotch until they agree.
-
Ew, do not want.
-
```
Select Case True
Case x = y
' Do stuff
Case a = b
' Do other stuff
Case Else
' More stuff
End SelectThat's so back-asswards it hurts. I don't mean the language feature, but how it was used here.
-
I'm dubious of any facts coming from blakeymouth. Can anyone else confirm that?
It's a standard blakeyfact. No confirmation needed.
-
I know. I'm just curious if it's really like that on the other side of the pond.
-
That's so back-asswards it hurts. I don't mean the language feature, but how it was used here.
I'm no fan of that pattern, it was chosen as an example of something that's miles outside what
switch
was ever intended to do. VB'sSelect Case
is more a replacement for nestedIf
s thanswitch
was ever intended to be.
-
I don't see the nested part here. Did you mean chained?
-
Select Case x Case 4 ' Do stuff 1 Case > 20 ' Do stuff 2 Case Else ' Do stuff 3 End Select
... is the equivalent of ...
If x = 4 Then ' Do stuff 1 Else If x > 20 Then ' Do stuff 2 Else ' Do stuff 3 End If End If
Chained ifs are simply nested ifs that don't use curly braces around the inner ifs. It's not different syntax, just a style that improves readability.
-
Chained ifs are simply nested ifs that don't use curly braces around the inner ifs. It's not different syntax, just a style that improves readability.
Yes, chained ifs are just special case of nested ifs, but they're so common and use case is so different from other nested ifs I had to point it out. Also, they're the only kind of nested ifs that map easily to select statement.
-
For chained
If
s, Visual Basic has the keywordElseIf
.I think they're handled just as nested
If
s, but they are handled differently by the editor (allElseIf
s are the same indentation level).IIRC the documentation says the difference to
Select Case
is that inSelect Case
the switch expression is evaluated only once.
-
I wonder if you could use LINE to make that code less magic-number ridden.
Or
__COUNT__
, if you have a C99-compatible preprocessor (or is it C11? CBA to check...)
-
IIRC the documentation says the difference to Select Case is that in Select Case the switch expression is evaluated only once.
Different to what? I can't think of a language where there is a similar construct, but the expression is evaluated more than once. In C#, the switch expression is evaluated once at run time.
case
supports expressions, but they are limited to expression that can be evaluated at compile time.
-
-
Different to what?
The chained
if
(If cond1 ... Then ... ElseIf cond2 Then ... ElseIf cond3 Then ... Else ... End If
).If the condN are of the form
switchExpr = exprK
, which makes the chainedif
most similar toSelect Case
, switchExpr will be evaluated every single time.In C#, the switch expression is evaluated once at run time. case supports expressions, but they are limited to expression that can be evaluated at compile time.
In Visual Basic, they can be arbitrary expression. Even of a type that isn't convertible to the type of the switch expression – that will just throw an InvalidCastException.
-
In Visual Basic, they can be arbitrary expression. Even of a type that isn't convertible to the type of the switch expression – that will just throw an InvalidCastException.
Remember, I'm the guy who provided the example that abused this property to the horror of @Gaska and @swayde.
-
Remember, I'm the guy who provided the example that abused this property to the horror of @Gaska and @swayed.
Point taken
-
Hm, I didn't know that one. Might play with it for this, as it would enable two yields on one line.
Will require some macro trickery though - that code does really need the same number twice in the generated C code. I mean, you could do
#define YIELD(ret) *__yield_pos=__COUNTER__+1; return (ret); case __COUNTER__:
but that's just ugly. And still means you're skipping positions. Using all consecutive numbers might allow for better optimisation by the compiler.
-
If the condN are of the form switchExpr = exprK, which makes the chained ifmost similar to Select Case, switchExpr will be evaluated every single time.
That's why I did
Select Case True
and put my expressions in theCase
s. In this form, it functions exactly likeIf
.
-
but that's just ugly.
The usual way to deal with that is a two-level macro:
#define YIELD(ret) YIELDLOC(ret, __COUNTER__) #define YIELDLOC(ret,ctr) *__yield_pos = (ctr); return (ret); case (ctr):
TIL about
__COUNTER__
…
-
That's why I did Select Case True and put my expressions in the Cases.
I've done this occasionally in SQL.
decode(true, cond1, expr1, cond2, expr2, ...)
That was mainly before I got comfortable withcase when cond1 then expr1 when cond2 then expr2 ... end
though.But there are times when this sort of construction is useful. I've used it in Informatica as well, because
DECODE(True, condition1, result1, condition2, result2, condition3, result3, ... )
is a bit easier to read and maintain than
IIF(condition1, result1, IIF(condition2, result2, IIF(condition3, result3, ... )...)))
especially when there's a lot of sub-casing going on (i.e. when a lot of the
result
s are actually IIF() statements in their own right).
-
TIL about COUNTER…
It's almost as if someone on the standards committee thought: "the use of
__LINE__
as a disambiguator in macro output is great and all, but what if I want to have several instances of the same macro on one line?"COUNTER?
-
@antiquarian said:
You are aware that "everyone" includes "idiots", right?
Yes, I post on this forum, don't I?
That's a misleading example, as in the context of web fora, 'everyone' is a proper subset of 'idiots'.
-
@PleegWat said:
but that's just ugly.
The usual way to deal with that is a two-level macro:
#define YIELD(ret) YIELDLOC(ret, __COUNTER__) #define YIELDLOC(ret,ctr) *__yield_pos = (ctr); return (ret); case (ctr):
TIL about
__COUNTER__
…Like I said, some trickery. I didn't know about counter either until tar hinted me that way.
counter seems to start at zero though, so either use -1 for the initial case or add one to the counter when using it here. You can't draw the initial case value from the counter because you need to know it to initialize your state.
-
That's a misleading example, as in the context of web fora, 'everyone' is a proper subset of 'idiots'.
There are idiots that aren't part of "everyone"?
-
This post is deleted!
-
Good point. Perhaps I should have said that the set of everyone is equal to the set of idiots?
Come to think of it, that works even outside of the context of Internet fora...
-
counter seems to start at zero though, so either use -1 for the initial case or add one to the counter when using it here
Or use it a few times (and throw away the value) and use zero as your initial state.
-
I've found yet another small annoyance: you cannot call lambda immediately after declaration. Because apparently lambdas aren't really functions - you must first construct a Func<> or some other function object from them, and only then they're callable.
-
you cannot call lambda immediately after declaration
Can you give an example, please? I can't imagine what you mean.But I found another annoyance: I wonder whether lambdas are treated as a kind of macro sometimes.
func<int, int> f = (x => x * x); var x = f(5);
won't compile because of a name conflict. (The name of a bound variable isn't abstracted, obviously.)
(f can't be defined generic, either.
func<T, T>
won't compile.)
-
-
won't compile because of a name conflict. (The name of a bound variable isn't abstracted, obviously.)
That happens because C# doesn't support variable shadowing.