𝄯 Sharp Regrets: Top 10 Worst C# Features


  • BINNED

    @ScholRLEA said:

    Filed under: I'm expecting a request to define 'read-macro' in 3, 2, 1...


  • Discourse touched me in a no-no place

    @cartman82 said:

    Don't know what he means here.

    Probably that you can many times use foreach instead, like when dealing with enumerables.



  • @xaade said:

    Value and Referential equality is a bitch.
    It's a bitch in every language, and no language does it right.

    C++ seems to do it well enough. Pointer equality means the object is the same, reference equality means two (possibly different) objects have equal value. Or are you talking about something else?

    So, say you have a pointer and a reference, and you want to know, A: are these two the same object? and B: do these two things have the same value?

    classPtr is a pointer to an object of a given class
    classPtr2 is another pointer to an object of that class
    classRef is a reference to an object of that class
    classRef2 is another reference to an object of that class

    if (classPtr == &classRef) the two objects are the same object (addresses are the same)

    if (*classPtr == classRef) the two objects have the same value

    if (classRef == classRef2) the two objects have the same value

    if (&classRef == &classRef2) the two objects are the same object

    if (classPtr == classPtr2) the two objects are the same object

    if (*classPtr == *classPtr2) the two objects have the same value



  • @hungrier said:

    I don't know what the "standard" is but most Winforms tutorials I've seen use btnOk, lstItems, etc prefix naming.

    The authors of those tutorials should be burned and any information contained in them should be considered suspect.



  • @xaade said:

    Then give me a counter in the foreach. I'm tired of explicitly writing counters or indexes for for loops because foreach loops fail to provide something that's so obvious.

    Just foreach() your loop, then ToList() the thing you foreached and grab its .Count.



  • That's an extra loop just to get the count. Sounds like a waste.



  • Well do it before you do the foreach then. The foreach is gonna enumerate the collection regardless, so it's not like adding a .ToList() to whatever your iterating through hurts anything.

    My point mainly is what the complaint is petty and easily fixed without resorting to the shitty for(;;) syntax which sucks.



  • That covers standard CL macros, but it doesn't seem to mention reader macros which are a bit different. Hmmmn.

    Basically, a read-macro (or reader macro, more correctly) is a macro which, instead of recognizing on the tokenized symbols, recognizes on the raw character stream, which means that it can change the meaning of individual characters, even if they appear inside or next to a different symbol. They are mostly used for adding single character syntactic elements to the language; for example, in Common Lisp the quote syntax (e.g., 'my-symbol for a quoting of my-symbol) is usually implemented as a reader macro that expands to the special form (quote my-symbol). They are very powerful, but can be even harder to debug than regular CL macros, and have a hellacious potential for abuse, not to mention being a major pain to implement, which is why both Clojure and standard Scheme don't have them (though some Scheme implementations do).

    My guess is that the reader macros LoL mentions expand the curly braces into a lambda form of some kind, which means that they are in effect local environments, mimicking the behavior in C.



  • What @xaade should probably use is the version of Select that gives you the index for free.



  • @xaade said:

    If you don't like it, don't use it.

    I mostly in agreement there.

    Unfortunately, there are things you can't just not use. They will never go away.

    Null references in particular. Even once something is added to mitigate null references, you have to remember to use that mechanism everywhere.


  • BINNED

    @ScholRLEA said:

    That covers standard CL macros, but it doesn't seem to mention reader macros which are a bit different.

    I totally forgot about those. It's been a while since I wrote any CL and I never wrote any read macros.



  • @NedFodder said:

    What @xaade should probably use is the version of Select that gives you the index for free.RTFM

    it's not empty!


  • Java Dev

    Regarding for, in 99.9% of the cases where you are using the three-clause version you really should be using an iterator or a while loop. Specifically in the 'two matching lists' case, you almost certainly need a while loop so yuo can handle mismatches in the body. For integer ranges, I believe some C-like languages have a range() function that returns an iterator.

    Regarding enums and flags, I've always liked the construction I first encountered in delphi and which probably originates from pascal, where you can define a flagset based on an existing enum.
    I don't think enums should transparently convert to integers, but conversion should be possible through casts or a function call.
    In C, I like being able to use an enum as the basis of a switch statement and getting compiler warnings if I missed some values. That is less relevant in an OO language though - if you're switching on an enum a lot you probably need subclasses.



  • @blakeyrat said:

    Well do it before you do the foreach then.

    I should have said "index" instead of "counter".

    @NedFodder said:

    What @xaade should probably use is the version of Select that gives you the index for free.

    This does what I need



  • @xaade said:

    I should have said "index" instead of "counter".

    If you need that, you're probably doing something wrong.



  • I fundamentally disagree.

    There are cases where you need an index in your iteration.


  • Banned

    10: The empty statement does nothing for me

    This is your complaint? Imagine what it would be if it was doing something!

    9: Too much equality

    Agreed. IComparable<T> should automatically give you all the operators (the equality and inequality operators should belong to IEqualable<T>, which IComparable<T> should implement).

    8: That operator is shifty

    Your idea of negative shifting is even worse.

    7: I'm a proud member of lambda lambda lambda

    Agreed. But you can't really hold a grudge against developers if you can safely ignore this whole old syntax.

    6: Bit twiddling entails parentheses

    TRWTF is bit twiddling in managed language.

    5: Type first, ask questions later

    Is this guy pretending he'd never seen another C-based language in his whole life?

    4: Flag me down

    One of reasons why I think there shouldn't be bitwise operators in managed languages.

    3: I rate plus-plus a minus-minus

    The complaint itself is perfectly fine, but the text underneath makes you look like an idiot.

    2 I want to destruct finalizers

    I have too little C# experience to comment.

    1 You can't put a tiger in the goldfish tank, but you can try

    Agreed.

    The for loop has bizarre syntax and some very rarely used features, is almost completely unnecessary in modern code, and yet is still popular.

    Blame C.

    Use of the += operator on delegates and events has always struck me as weird. It also works poorly with generic covariance on delegates.

    Agreed.

    The colon (:) means both "Extends this base class" and "Implements this interface" in a class declaration. It's confusing for both the reader and the compiler writer. Visual Basic spells it out very clearly.

    And + means either a bigger or smaller number, depending on the sign of right-hand value.

    The rules for resolving names after the aforementioned colon are not well founded; you can end up in situations where you need to know what the base class is in order to determine what the base class is.

    Wut?

    The void type has no values and cannot be used in any context that requires a type, other than a return type or a pointer type. It seems bizarre that we think of it as a type at all.

    Blame C - specifically, the part of C you criticized in #5.

    Static classes are how C# does modules. Why not call them "modules"?

    Because fuck you, that's why! No matter the name, it will still be a dirty hack just to allow something that other languages call global functions.

    No one would shed tears if the unary plus operator disappeared tomorrow.

    You underestimate how bad legacy code is.

    Also, I ignored about 40 of last posts because I couldn't stand this for argument but also wanted to express how low I think of the author of the article.



  • @xaade said:

    There are cases where you need an index in your iteration.

    Give me an example so I can tell you how you're wrong.



  • @Gaska said:

    TRWTF is bit twiddling in managed language.

    Custom protocol code. Binary serialization.

    @Gaska said:

    Is this guy pretending he'd never seen another C-based language in his whole life?

    Yeah, he's being very, "I have a hammer", here.

    @Gaska said:

    One of reasons why I think there shouldn't be bitwise operators in managed languages.

    Using flags in enumerators works fine.

    @Gaska said:

    Agreed.

    It has good semantic meaning for an event that can have multiple subscribers.

    @Gaska said:

    "Extends this base class"

    People spend more time trying to explain how you think of something determines how well your patterns will be.

    Rather than just being more explicit.

    "Extends"
    "Inherits"

    Meh... I never put much stock in the difference.

    You could say, "Extends this interface"

    Or you could come up with a word that makes sense for both.

    What I don't want is a language that focuses so much on explicitness that I have to write a paragraph.

    class A extends Base and implements FlyerInterface.

    It's called balance.



  • @Gaska said:

    Agreed. But you can't really hold a grudge against developers if you can safely ignore this whole old syntax.

    It's not a grudge as such. He's one of the developers of this, and these are just the things he's least satisfied with. He's definitely among the group of people with a right to complain.



  • @Gaska said:

    Agreed. But you can't really hold a grudge against developers if you can safely ignore this whole old syntax.

    Well, being the member of C# design commitee, I doubt he can.

    @Gaska said:

    TRWTF is bit twiddling in managed language.

    How's that different from an unmanaged language? If you bit-twiddle your pointers, YDIW anyway.

    @Gaska said:

    One of reasons why I think there shouldn't be bitwise operators in managed languages.

    public enum Font
    {
       Bold,
       Italic,
       BoldItalic,
       Underline,
       BoldUnderline,
       ItalicUnderline
       //...
    }
    

  • Trolleybus Mechanic

    @blakeyrat said:

    Give me an example so I can tell you how you're wrong.

    Not saying this is "right" or "good", but I've seen this:

    SomeCollectionOfObjectsAndShit
    SomeCollectionOfUIElementsOneForEachObject
    SomeOtherCollectionOfUIElementsOneForEachObject

    index = 0;
    foreach shit
    if(shit.color = brown)
    ui[index].colorlabel = "normal"
    else
    ui[index].colorlabel = "What the fuck have you been eating?!?"

    if(shit.solidindex > 4)
    otherui[index].consistencylabel = "normal"
    else
    otherui[index].consistencylabel = "stop eating so much fucking soup"

    index++;
    

    end foreach



  • A more valid use case is with, say, Excel reports - you need the row number to know which row you're writing into. In yours, you can probably abuse Zip or something LINQ-ish (though those normally shouldn't have side effects, but weeeeell, we've all done that).


  • Banned

    @xaade said:

    Custom protocol code. Binary serialization.

    OK, these are legitimate uses.

    @xaade said:

    Using flags in enumerators works fine.

    And that's the problem. I'm not sure you've noticed but this point was about assigning arbitrary integer values to enum variable.

    @xaade said:

    It has good semantic meaning for an event that can have multiple subscribers.

    The main complaint about C++'s streams is that they use bithift operators for reading and writing to stream instead of methods. It's the same thing as here.

    @xaade said:

    You could say, "Extends this interface"

    Or you could come up with a word that makes sense for both.


    You could say "is subtype of". Or you could stop trying to make verbatim translations from programming to human languages make perfect sense.

    @Magus said:

    It's not a grudge as such. He's one of the developers of this, and these are just the things he's least satisfied with. He's definitely among the group of people with a right to complain.

    Well, the article looked like some noob programmer who spent one year fiddling with C# and knows nothing about any other language, not one of people responsible for making C# so great. Mea culpa.

    @Maciejasjmj said:

    How's that different from an unmanaged language? If you bit-twiddle your pointers, YDIW anyway.

    It's about the level of abstraction. What's OK in asm isn't always OK in C, and similar rule applies to higher level languages.


  • Winner of the 2016 Presidential Election

    [quote="FTA @3"]
    When the operand is a variable, this is the actual behavior:

    1. Both operators determine the value of the variable.
    2. Both operators determine what value will be assigned back to storage.
    3. Both operators assign the new value to storage.
    4. The postfix operator produces the original value, and the prefix operator produces the assigned value.

    [/quote]

    [quote="FTA @3"]
    In C#, the user-defined increment and decrement operators return the value to be assigned; they don't mutate the storage.
    [/quote]

    :wtf: ❓
    WhyTF is the user-defined version expected to operate that differently from the standard version? Am I misunderstanding something?



  • @Lorne_Kates said:

    Not sa

    @Maciejasjmj said:

    A more valid us

    That's great, but neither of you clowns is Mr. Gamergate Xaaaaaaaaaaaade. Don't white-knight for that asshole, he's a big boy, he can speak for himself.



  • @Gaska said:

    The main complaint about C++'s streams is that they use bithift operators for reading and writing to stream instead of methods. It's the same thing as here.

    Which also makes semantic sense.

    You could also use the operator for push and pop semantics.

    But, if you want to criticize operators, the -> pointer and . operators make just about as much sense.

    Consistency matters more.

    @Gaska said:

    You could say "is subtype of".

    I think we're agreeing here.



  • @Gaska said:

    This is your complaint? Imagine what it would be if it was doing something!

    I think that was supposed to be funny.

    Anyways, one could just as well make nop; the syntax for the empty statement (so ASM!).


  • Banned

    @xaade said:

    Which also makes semantic sense.

    Any syntax makes semantic sense if you believe strongly enough.


  • Banned

    @xaade said:

    But, if you want to criticize operators, the -> pointer and . operators make just about as much sense.

    Care to elaborate? What's wrong with one or the other? Are we still talking about C#?



  • @Gaska said:

    It's about the level of abstraction. What's OK in asm isn't always OK in C, and similar rule applies to higher level languages.

    So by "managed languages", you actually mean "modern high-level languages".

    And even then it doesn't make much sense. Occasionally, you need a bit operation for your algorithm, and there's no getting around it - how are you going to do that, have a built-in IL method on int? Loop over the bits? That's bizarre.


  • Banned

    @Maciejasjmj said:

    So by "managed languages", you actually mean "modern high-level languages".

    Show me a managed language that isn't modern or high-level. C++/CLI and esoteric languages don't count.

    @Maciejasjmj said:

    And even then it doesn't make much sense. Occasionally, you need a bit operation for your algorithm, and there's no getting around it - how are you going to do that, have a built-in IL method on int? Loop over the bits? That's bizarre.

    If I was designing language, I'd go with real bitflag types - declared like enums but with flag or some other keyword instead of enum, and with overloaded + and - operators (and matching += and -= operators) for adding and removing flags.

    As for bitshifts, a good alternative is a packed struct with field size declared in bits.



  • Well even if there isn't, which I doubt (but CBA to check which languages use a runtime and which don't), it still has fuckall to do with being managed or not.

    And flags aren't the only case for bit operations. Hashing, encryption, compression, hardware interfaces and a lot of other things also require bitwise operations.



  • @cartman82 said:

    > ####1 You can't put a tiger in the goldfish tank, but you can try
    This feature is called "array covariance," and it allows developers to deal with the situation in which they have an array of goldfish in hand, they have a method they didn't write that takes an array of animals, the method only reads the array, and they don't want to have to allocate a copy of the array. Of course, the problem arises if the method actually does write to the array.

    Good point. I remember getting fucked by this a few times.

    Actually I consider this a symptom of my main grief with C# (and Java), the lack of const (for references).

    Because covariance is correct for the read interface of an array. However write interface is contravariant. If C# had const, assigning Goldfish[] to const Animal[] would be correct. And helpful and safe at the same time. However because it does not, it was left with two bad choices. Allow assigning Goldfish[] to Animal[] and have setting elements throw or not allow Goldfish[] to Animal[] and require copies all over the place.

    Of course the const is much more widely useful beyond this corner case. Most of the time you expect methods to only modify their invocant, but not their parameters. In C++ such promise can be made obvious by taking a const reference and it will be checked by the compiler. In C# you have to pray that the author of the other component followed conventions—or clone the object.

    @cartman82 said:

    @dkf said:
    Some languages have a unit type, with exactly one value of that type (also called unit usually). Since there's only one value, it doesn't need to be communicated around as it cannot actually be carrying any information. It works like void but without the suck!

    So like undefined.

    undefined is a thing of dynamic languages where everything is of type “anything” and undefined is a special value meaning there is nothing there. Python's None is a better name for that concept, because it really signifies it is known there is nothing of interest rather than it would not be known what should be there.

    In statically typed languages it is different, because items that have the unit type can only ever contain the one value, so the compiler can eliminate any variables of that type, and items of any other type can't contain that value, so it does not come up at unexpected places like undefined does in JS.

    @xaade said:

    > ####5: Type first, ask questions later

    This is really a grass is greener argument here.

    It is the wrong argument. The reason the ‘type name’ order in C was a mistake is that it is hard to parse and that is why most new languages are returning to the Pascal ‘(var) type : value’. For readability, I don't see any significant difference.

    @xaade said:

    Then give me a counter in the foreach. I'm tired of explicitly writing counters or indexes for for loops because foreach loops fail to provide something that's so obvious.

    That can be done with a simple wrapper that takes an enumerator and returns an enumerator that returns tuple of index and value. Unfortunately without deconstructing assignment (unless they added one recently; I didn't work in C# for couple of years) it is still rather unwieldy.

    Of course most of the time you don't need index, you need mutable iterator. I hated indexes since I worked in C (I preferred iterating with pointer to iterating with integer) and I almost never need them in C++ with its iterators.

    @Kian said:

    @xaade said:
    Value and Referential equality is a bitch.
    It's a bitch in every language, and no language does it right.

    C++ seems to do it well enough.

    It still requires you to overload each operator separately though.

    A good way is that you define one “compare” function and all the operators automatically get appropriate implementations delegating to that.

    I would argue that there are languages that do it right, for example Rust (additionally it supports partial ordering, so it has PartialEq, Eq, PartialOrd and Ord).

    Surprisingly Python 2 seems to have made it a bit easier with its __cmp__ where Python3 needs to use library utility functools.total_ordering. If my memory serves me well, Perl supports it with it's overload pragmatic module. And of course for C++ there is Boost.Operators, but since that is not standard library, it does not really count.


  • Banned

    @Maciejasjmj said:

    Well even if there isn't, which I doubt (but CBA to check which languages use a runtime and which don't), it still has fuckall to do with being managed or not.

    Being managed implies being completely detached from underlying hardware. I could say "high-level", but this term just isn't strong enough here.

    @Maciejasjmj said:

    And flags aren't the only case for bit operations. Hashing, encryption, compression, hardware interfaces and a lot of other things also require bitwise operations.

    Ideally, those tasks should be performed by libraries written in some other, less "OOP-y" language, designed specifically to deal with streams of binary data. But I can see how some people might want to have one language for everything (not that I approve), so I guess this is kinda sorta justified. Except the hardware interfaces part.


  • Discourse touched me in a no-no place

    @Bulb said:

    undefined is a thing of dynamic languages where everything is of type “anything” and undefined is a special value meaning there is nothing there. Python's None is a better name for that concept, because it really signifies it is known there is nothing of interest rather than it would not be known what should be there.

    In statically typed languages it is different, because items that have the unit type can only ever contain the one value, so the compiler can eliminate any variables of that type, and items of any other type can't contain that value, so it does not come up at unexpected places like undefined does in JS.

    Thank you for making this argument more clearly than I did.



  • @Bulb said:

    In statically typed languages it is different, because items that have the unit type can only ever contain the one value, so the compiler can eliminate any variables of that type, and items of any other type can't contain that value, so it does not come up at unexpected places like undefined does in JS.

    It would probably end up a bit nasty in C# and Java due to the type hierarchy having a root in o/Object. Either you don't follow it and end up special-casing a lot, or you follow it and end up with a potential for some really bizarre usages.

    @Gaska said:

    Being managed implies being completely detached from underlying hardware.

    Even if I were to subscribe to that, what does bit-fiddling have to do with that?

    @Gaska said:

    I could say "high-level", but this term just isn't strong enough here.

    Those are two totally separate concepts. MSIL is low-level, but managed as much as C#. C++ is high-level, but not managed at all save for the MS flavours.

    @Gaska said:

    Ideally, those tasks should be performed by libraries written in some other, less "OOP-y" language, designed specifically to deal with streams of binary data.

    So you want to force the projects to use two separate languages, with both personal/project management and technical problems this entails, just because bit operations ain't right to you in C#?


  • Discourse touched me in a no-no place

    @Maciejasjmj said:

    It would probably end up a bit nasty in C# and Java due to the type hierarchy having a root in o/Object.

    You'd be OK in Java, as you could make it be a non-reference type and they live outside the hierarchy. No idea about C#, though I think that tries to be more consistent and that causes issues for this…



  • @Maciejasjmj said:

    And flags aren't the only case for bit operations. Hashing, encryption, compression, hardware interfaces and a lot of other things also require bitwise operations.

    And bit arrays are very seldom used as numerical values you do arithmetics with, so I'd prefer to have a different type besides integer for bit arrays.

    Edit: If you really need to do bitwise operations on an integer number, or arithmetics on a bit array, there's still typecast.



  • @Maciejasjmj said:

    @Gaska said:
    One of reasons why I think there shouldn't be bitwise operators in managed languages.


    public enum Font
    {
    Bold,
    Italic,
    BoldItalic,
    Underline,
    BoldUnderline,
    ItalicUnderline
    //...
    }

    I agree with Gąska in this point - all that bit fiddling should be left to the compiler.

    With a flag enum type (as opposed to a simple enum type) it wouldn't be necessary to define all possible values but only the values we want to have flags for.

    Internally, the operators are bitwise operators of course, but to the programmer, they should be called flag operators that are independent of which bit a flag is stored in. (They could look like bitwise operators, of course, but should be conceptionally different from operators working on bit arrays.)


  • Banned

    @Maciejasjmj said:

    Even if I were to subscribe to that, what does bit-fiddling have to do with that?

    Bits are implementation detail of integers. Just like you shouldn't have to know concrete type of object behind an interface, you shouldn't have to know that integers are made of bits.

    @Maciejasjmj said:

    Those are two totally separate concepts. MSIL is low-level, but managed as much as C#. C++ is high-level, but not managed at all save for the MS flavours.

    Depends on what's your definition of high- and low-level. CIL is pretty high-level, looking at the instruction set - some would argue it's higher level than C, and C is part of C++.

    And BTW - MSIL is pretty object-oriented, so even in this language, bit-fiddling seems hacky to me - but at least no one has to see those hacks.

    @Maciejasjmj said:

    So you want to force the projects to use two separate languages, with both personal/project management and technical problems this entails, just because bit operations ain't right to you in C#?

    No - I want the best tool available for the given job. In ideal world, FFI is not a problem, so you can mix languages freely without problem. C#, just like Java, Haskell, Python etcetera etcetera, are simply not designed for the jobs that require bit fiddling. Yes, you can do it, just like you can take highway in Fiat 126, but the result is not too pretty.



  • @Gaska said:

    Bits are implementation detail of integers. Just like you shouldn't have to know concrete type of object behind an interface, you shouldn't have to know that integers are made of bits.

    From a puritan standpoint, maaaaybe, though it's not as much "implementation detail" as "common knowledge". But that's how literally every language in the world works, people are used to numbers being twiddlable, and on the bottom line you're still ANDing two integers on the processor - but now you need to convert into them from bit arrays and vice versa. And those bit arrays pretty necessarily take at least 1 byte per bit, or more - I know memory and cycles are cheap, but come on, that's just a waste of power.

    @Gaska said:

    And BTW - MSIL is pretty object-oriented, so even in this language, bit-fiddling seems hacky to me - but at least no one has to see those hacks.

    Well, if you go lower, you get into unmanaged and platform-specific code.

    @Gaska said:

    In ideal world, FFI is not a problem, so you can mix languages freely without problem.

    We don't live in an ideal world. Even CLR interop can get wonky, not to mention native.

    @Gaska said:

    C#, just like Java, Haskell, Python etcetera etcetera, are simply not designed for the jobs that require bit fiddling.

    Like... writing algorithms? Seriously? I don't know where you get the idea that everything written in a high-level language is business stuff, and everything written in a low-level language is arcane fuckery. People go around solving Project Euler problems in languages they're comfortable with all the time, they implement libraries in those languages which provide algorithms, etc, etc.

    @Gaska said:

    Yes, you can do it, just like you can take highway in Fiat 126, but the result is not too pretty.

    But you're not buying an Audi just because your route takes you through all 5 kilometers of Polish highways.



  • @Maciejasjmj said:

    From a puritan standpoint, maaaaybe, though it's not as much "implementation detail" as "common knowledge"

    I wouldn't even call it an implementation detail, since in C# int is a synonym for Int32, which is pretty darn specific about its bits.


  • Banned

    @Maciejasjmj said:

    But that's how literally every language in the world works, people are used to numbers being twiddlable

    And that's sad - especially in JavaScript, where you're bit-shifting doubles. But in other languages, I don't like it either, because of conceptual reasons.

    @Maciejasjmj said:

    on the bottom line you're still ANDing two integers on the processor - but now you need to convert into them from bit arrays and vice versa.

    Once you start working with actual bit arrays, you have no reason to ever convert them to integers - except for the last stage where you deserialize data into proper object, but here you cannot prevent copying around large swathes of memory anyway, regardless if you're doing bit-fiddling on ints or bits.

    @Maciejasjmj said:

    And those bit arrays pretty necessarily take at least 1 byte per bit, or more

    I can't see how that's necessary. C++'s std::vector<bool> stores bits in single bits just fine.

    @Maciejasjmj said:

    Well, if you go lower, you get into unmanaged and platform-specific code.

    Yes, exactly.

    @Maciejasjmj said:

    We don't live in an ideal world.

    Yes, we don't. I thought this was so obvious it didn't even need mentioning. But discussing stuff in ideal world, unconstrained by backwards compatibility, hardware quirks and stupid management, is far more interesting.

    @Maciejasjmj said:

    Like... writing algorithms?

    The very specific class of algorithms that require bit fiddling. Which are always task on such a low level that a code in high-level language will always look bad and have many subtle quirks.

    @Maciejasjmj said:

    People go around solving Project Euler problems in languages they're comfortable with all the time, they implement libraries in those languages which provide algorithms, etc, etc.

    There are people laying under trains too. The fact something was done doesn't mean it's good. Take a look at this Project Euler Problem 6 solution. This guys seems pretty comfortable with Brainfuck. Surely it must be a good language for solving mathematical problems, don't you think?

    @Maciejasjmj said:

    But you're not buying an Audi just because your route takes you through all 5 kilometers of Polish highways.

    If money weren't a problem, I probably would.

    @Salamander said:

    I wouldn't even call it an implementation detail, since in C# int is a synonym for Int32, which is pretty darn specific about its bits.

    LEAKY ABSTRACTIONSSSSSSSSSSS



  • @Gaska said:

    If I was designing language, I'd go with real bitflag types - declared like enums but with flag or some other keyword instead of enum, and with overloaded + and - operators (and matching += and -= operators) for adding and removing flags.

    As for bitshifts, a good alternative is a packed struct with field size declared in bits.

    UM seriously.

    += to add a subscriber is bad.

    But += to add a flag is ok?

    Consider a value with two flag, IsEnabled and IsRightToLeft.
    IsEnabled is represented internally as 1
    IsRightToLeft is represented internally as 2.

    var MyFlag = MyFlags.IsEnabled;
    MyFlag += MyFlags.IsEnabled;
    
    //If flags were to work correctly.
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsEnabled)); // outputs true
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsRightToLeft)); // outputs false
    
    //If flags were to work as I'd expect with operators +=
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsEnabled)); // outputs false
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsRightToLeft)); // outputs true
    

    You've created something that is semantically worse than += used for subscribing to events.
    Because if you do this

    void Main()
    {
        var a = new ObjectWithEvent();
        a.Event += Subscriber;
        a.DoThingThatTriggersEvent();
    
        // expected result? "triggered" is output twice.
        // which makes sense, you added a subscriber twice. So it reacts twice.
    }
    void Subscriber(object sender, EventArgs e)
    {
        Console.WriteLine("triggered");
    }
    


  • @Bulb said:

    The reason the ‘type name’ order in C was a mistake is that it is hard to parse and that is why most new languages are returning to the Pascal ‘(var) type : value’. For readability, I don't see any significant difference.

    var a = new Thing();

    Can we move on now?

    @Bulb said:

    It still requires you to overload each operator separately though.

    I wonder if you can overload in class extensions?


    I mean seriously, I don't get all this hate for operators.

    For me, operators are far less cumbersome, even if everyone disagrees that the semantics make PERFECT sense.

    a.Plus(b).Minus(c); // oh god please no


    I could make other arguments too.

    Like, why is -> necessary.

    In what world ever will you have a type that can be accessed with . and ->
    And even if you say there are some types that the compiler can't know are pointers, you can't access much from -> in those anyway until you cast it to another type.


  • Banned

    @xaade said:

    UM seriously.

    += to add a subscriber is bad.

    But += to add a flag is ok?


    Yes, because A += B should be equivalent to A = A + B, and A + B shouldn't modify its arguments.

    @xaade said:

    var MyFlag = MyFlags.IsEnabled;
    MyFlag += MyFlags.IsEnabled;

    //If flags were to work correctly.
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsEnabled)); // outputs true
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsRightToLeft)); // outputs false

    //If flags were to work as I'd expect with operators +=
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsEnabled)); // outputs false
    Console.WriteLine(MyFlag.HasFlag(MyFlags.IsRightToLeft)); // outputs true


    Why do you expect flags to work like integers? If you have two sets, A = {1} and B = {1}, do you expect A+B = {2}?



  • Ok this post is a bit weird because I wrote it from bottom to top. WP10 allows select-quoting, but it always adds the new quote in at the top of the box and I cba to fix it.

    @Maciejasjmj said:

    It would probably end up a bit nasty in C# and Java due to the type hierarchy having a root in o/Object

    The biggest problem with c# right now (well, this or reference types being nullable by default), and it doesn't even make the list? It was a shitty hack from the start, only necessary because they didn't have generic types, and if there's one thing that's going to obsolete c# and Java altogether, it's going to be their rooted type tree.

    Enumerated types 4 lyfe COMPLAINES!

    @Yamikuronue said:

    I feel like all the valid criticisms would go away if you defined ++ to return void rather than return a value at all, in both prefix and postfix positions

    I disagree. My opinion is that removing the redundant pre-increment operator is all it would take to make any confusion about ++ semantics disappear altogether.

    @cartman82 said:

    Just never rely on its strange prefix/postfix semantics and you're good.

    I relied on post-increment semantics today, unashamedly. Fuck that pre-increment operator though, right in it's little.

    @cartman82 said:

    What's up with all the ++ hate? That thing is just a pleasure to type and is very distinct when looking at code.

    ❤.

    @dkf said:

    However, if we stick to just Eric's top ten: array covariance is fundamentally broken. It's broken in Java too. Some design decisions are just plain wrong

    I did this today:

    @SuppressWarnings("unchecked")
    final T[] empty = (T[])new Object[0];
    

    My rational mind knows it won't break anything, but still it creeps me out, and if we didn't have a go live on Monday I would have taken the 5 minutes to figure out how to do it properly.


  • Banned

    @xaade said:

    I mean seriously, I don't get all this hate for operators.

    It comes from that most languages implement them badly. The most common mistake is that they don't enforce the semantics - for example, arithmetic operators shouldn't modify the arguments, ==, should return bool, and </<= should be direct opposites of >=/> respectively.



  • @Buddy said:

    I disagree. My opinion is that removing the redundant pre-increment operator is all it would take to make any confusion about ++ semantics disappear altogether.

    No, the post-increment operator needs to be banished to belgium. It makes so much less sense.


Log in to reply