tags:

views:

2303

answers:

31
+37  Q: 

.NET WTF?s

There's a ton of classes in the .NET framework. As much as I'd like to think all the engineers behind .NET are brilliant, sometimes, you'll learn how they've implemented something, and you think to yourself, WTF?

What are some of the things that have made you pause and ask yourself, "WTF?," while using the .NET library?

Here's an example:

Why does StreamReader inherit from TextReader, and not the other way around, and BinaryReader derive from neither? After all, a StreamReader could be reading any type of data, while a TextReader should only read text, you'd think, right?

How about some methods that should obviously (in your mind), be virtual, but can't be overridden?

+4  A: 

here's a few that leap to mind:

  • where are all of the logical interfaces for the base classes, e.g. IDataSet, IThread?
  • why is Thread sealed?
  • why is TableAdapter generated in full each time as a subclass of Component, instead of from a useful base class?
  • why does GridView have a ShowInsertButton option but really won't support inserting rows?
Steven A. Lowe
+11  A: 

I have no doubt that if .Net designers were to design it again, they would make some (or many) adjustments. the question is do you want to wait for the ideal framework to come to being before you start doing something or you want to start producing some apps today? at the end of the day, it's the programmer, not the tool that makes the difference.

my 2 cents Ali

Ali Shafai
I would do every project I've ever completed differently; I look back and hate the code I wrote even last week. But I would achieve nothing if I worked towards the 'perfect' solution because it doesn't exist.
CJM
As long as they keep developing real improvements and focus less on RAD that appear sexy for a 200 user/month solution, I'll be a happy camper.
Chris
-1 good programmers know that the tool *does* make a big difference - that is why we're using .net/VS in the first place, not coding C in notepad
BlueRaja - Danny Pflughoeft
+1  A: 

The BaseNumberConverter handles hex incorrectly because someone mistyped some parentheses (means it will accept hex numbers for doubles).

Jeff Yates
A: 

The biggest WTF to me is nothing in the framework itself, but rather a term they used a while ago.

Being able to call unmanaged code from managed code had the acronym IJW.... or in longer form, "It Just Works".... WTF!?!?! That makes me confident ;). But alas, all the work i ever did like that, did work, and quite well; but still the term was scary.

Something slightly less WTF is the amount of highly useful classes & methods that are marked sealed/private/internal.

mattlant
care to explain?
mattlant
Try to inherit from List<>, StringBuilder or Thread and you will see :-)
Michael Stum
+4  A: 

There is a handy Pair class for storing 2 related objects, but it's buried in System.Web.UI

Joe
You mean you missed System.Collections.Generic.KeyValuePair<TKey, TValue>? http://msdn.microsoft.com/en-us/library/5tbh8a42.aspxDon't worry, I did for a LOOOOOOONG time :P
Slace
Slace, 'key-value' doesn't neccessary describe the relationship.
TraumaPony
This one made me mad, too. Let's see: a generic class for storing two related objects? Very useful. Where does it belong? Of course! System.Web.UI. Where storing two related objects comes up all the time? Huh?
MusiGenesis
I believe Pair was created for ViewState, which is why it was put in that library.
Adam Lassek
Lucky there is Tuple class in C# 4.0http://msdn.microsoft.com/en-us/library/system.tuple(VS.100).aspx
Jakub Šturc
+1  A: 

The Microsoft team's love of XML. Don't get me wrong, I can see the uses of it for "enterprise" type applications, but for a lot of uses, it's just too much and its binary file support isn't a substitute for something human readable.

Jason Baker
Are you telling me you dont like xml? You can serialize any piece of data into a common format that you can give to any other interpreter. it is the tower of babble for developers.
Dested
And I can quickly store entire states of things, instead of putting it in a database and having to fish it out and put it back into an object, or parsing through some file like a monkey and putting it back into an object..why not just turn the object into xml and then back. Sorry SO made me split it
Dested
an XML can be enforced by a schema. A .txt file can't.
Andrei Rinea
+17  A: 

Many of these WTFs were described in the "Framework design guidelines" book. Brad Abrams (one of the book authors and PM of .NET FW) confessed that there were some problems in .NET design. .NET is HUGE, a lot of people were involved in it's development. In my opinion it's quality and consistency is still superb taking into account it's complexity and diversity.

There was a code analysis of .NET FW made by NDepend tool. And if I recall correctly metrics were not bad.

I really amazed with this solid software. I can forgive them some design glitches taking into account huge number of wonderful features in .NET.

aku
+1, use it daily... Sometimes I wonder how they managed to keep track of everything!
Pat
That's a superb book.
Bob King
+14  A: 

I know that there are reasons for this, but honestly I was a bit surprised when I figured out how Math.Round worked.

1.5 rounds to 2.

2.5 rounds to...2!?!?

By default Math.Round will round to the nearest EVEN number. I know that there is a lot of history and reasoning behind this, but it runs contrary to everything I've been taught about arithmetic.

Kevin Pang
It's exactly what I was taught, though. But it was around 1975, and I think they've changed that rule since then.
Thomas Padron-McCarthy
This rule is necessary so that x.5 isn't always rounded up or rounded down. Always rounding .5 in one direction would result in a persistent bias. Since rounding in computers is often used for financials, it makes sense that this is the default.
MusiGenesis
Correct, that is why it is often referred to as "bankers rounding". And yes, they changed the teaching. Most grade schoolers in the US are taught to round away from zero nowadays (or at least I was). :P
Kevin Pang
Try finding this out when you're also developing for the Compact Framework where they dropped the override where you can specify the type of rounding! Joy, so here is me working on a modern framework and I have to write my own rounding algorithm!
Quibblesome
So if the financial software that was being used in Office Space where they wrote a virus to put rounded fractions of pennies into a bank account would not have worked if the software rounded like this?
YWE
+3  A: 

CodeDom.

Why is almost every class in CodeDom sealed?

CodeMemberProperty should support separate visibility for the getter and the setter, but it doesn't. I also can't work around the design because (naturally) all the relevant classes are sealed.

The ternary operator is not supported in all .NET languages, so I have to generate the 3 expressions and then concatenate them together in a CodeSnippetExpression to be able to generate them. Why not support the operator, and then let the CodeDom

Some combinations of member attributes aren't distinct, and as such can't be generated, such as sealed override.

Argh, CodeDom.

Jacob
They are sealed so you cant mess it up :)
leppie
You think the absence of the ternary operator is surprising? Whay about Not? How many languages don't have that unary operator?
Damien_The_Unbeliever
"Not" being absent is also a huge pain. I forgot about that one, since I wrote a function Not(CodeExpression) that adds the "(false == {0})" for me
Jacob
+1  A: 

One my most puzzling WTF's is why the JavaScriptSerializer (in System.Web.Script.Serialization - http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx) has been made obsolete between the release of AJAX 1.0 and .NET 3.5.

It's the best way to serialize third-party object and a beautifully quick way to serialize any object over DataContractJsonSerializer

Slace
(Fixed in 3.5SP1, btw)
Marc Gravell
+6  A: 

I always thought the XmlWriter was a pretty insane design for an abstract base class. The following are just the abstract methods you have to provide - there are plenty of other virtual and non-virtual variations.

One of the things that bothers is that there are so many different ways callers can do the same thing. When you think you have a correct implementation, and you go to use it with an XmlSerializer, you always learn something new about caller expectations you're not satisfying.

But the biggest WTF has to be WriteRaw. I mean the XmlWriter is supposed to be the abstraction of the formatting of the infoset being written, but that method allows the caller to format any fragment into xml text and pass it in. What's a non-xml-text writer supposed to do? Parse the xml text fragment in context to the best of it's ability and call whichever methods should have been used in the first place?

public class CustomWriter : XmlWriter
{
    public override void WriteStartDocument() { }
    public override void WriteStartDocument(bool standalone) { }
    public override void WriteEndDocument() { }
    public override void WriteDocType(string name, string pubid, string sysid, string subset) { }
    public override void WriteStartElement(string prefix, string localName, string ns) { }
    public override void WriteEndElement() { }
    public override void WriteFullEndElement() { }
    public override void WriteStartAttribute(string prefix, string localName, string ns) { }
    public override void WriteEndAttribute() { }
    public override void WriteCData(string text) { }
    public override void WriteComment(string text) { }
    public override void WriteProcessingInstruction(string name, string text) { }
    public override void WriteEntityRef(string name) { }
    public override void WriteCharEntity(char ch) { }
    public override void WriteWhitespace(string ws) { }
    public override void WriteString(string text) { }
    public override void WriteSurrogateCharEntity(char lowChar, char highChar) { }
    public override void WriteChars(char[] buffer, int index, int count) { }
    public override void WriteRaw(char[] buffer, int index, int count) { }
    public override void WriteRaw(string data) { }
    public override void WriteBase64(byte[] buffer, int index, int count) { }
    public override void Close() { }
    public override void Flush() { }
    public override string LookupPrefix(string ns) { throw new System.NotImplementedException(); }
    public override WriteState WriteState { get { throw new System.NotImplementedException(); } }
}
loudej
This is what you get when you throw thousands of people into a monster-development-from-scratch in, what... three years? We should admit: even for Microsoft, it was an Herculean task. So... Now imagine the blood caffeine levels from these guys when they wrote the code above... :)
F.D.Castel
+3  A: 

From the .Not, the .Net hall of SHAME:

Problem:

System.Collections sucks. There has been an attempt design decent collection classes but the "engineer" gave up too soon. There is IList and IDictionary but there is no IQueue or ISet and too many classes in the .NET framework rely on concrete implementations of IList and IDictionary (such as ArrayList and Hashtable) rather than just use the abstract concepts of just IList and IDictionary. Any first year computer science student knows the importance of abstracting abstract data types (list, dictionary, stack) from their implementation (arraylist, hashtable, arraystack). One wonders where the guy who designed System.Collections "earned" his computer science degree...

Solution:

Refer to first year text books (or Java2 documentation) and write decent collection classes.

Jakub Šturc
Both ArrayList and IList are WRONG!Collections returned from a framework should be concrete so you can add methods to them later. For example, you can add a create widget method to a WidgetCollection, but not to an IList.
Jonathan Allen
+6  A: 

There is no interface that represents the basic arithmetic operations, on the other hand there is an IComparable interface for the (not so dissimilar) "Compare" operator. To make things worse, number classes and value types do not share a common ancestor.

Exceptions! FxCop and MSDN guidelines do not agree on how to properly write an exception (derive from Exception or ApplicationException)?

The WebService class. Why is it there at all? All ASMX functionalities are provided in a "wired-in" not configurable way via Reflection. What if I want to extend these? Not possible!

ConfigurationSection and ConfigurationElement. ConfigurationSection derives from ConfigurationElement, and can contain a ConfigurationElement. Therefore, one must assume that a ConfigurationSection can contain another ConfigurationSection. Which would be great: let's say you need to configure a class that uses another class. Each has a configuration section to specify the values. You should be able to make one ConfigurationSection contain the other accordingly. But, lo and behold! DotNet will allow you to do this at compile time but throw an error at runtime. WTF?

Sklivvz
Your last one is the worst. Truly evil.
MusiGenesis
The whole "inherit from ApplicationException" is a hangover from 1.0.It was supposed to separate out exceptions from the application and something else, IIRC this "best practise" was phased out so I think the MSN guidelines are now out of date if they still say that.
Quibblesome
The one with the exceptions is because first they recommended to derive from ApplicationException. Some time later they saw no benefit in this and began recommending to use Exception a the base class. Don't ask me who "they" are and where I read this, is must have been MSDN online or a book.
Mudu
+3  A: 

Welcome to the world of iterative, cross-team design.

The .Net team isn't just one team. It's a CLR team plus a compiler team plus a framework team plus an IDE team. Each has their own set of budgets, deadlines, requirements, technical restrictions... the works.

Whenever you see a WTF, it'd not a "durrrrrr", but a balance of time and resources to get a product out the door. Some challenges are easy in some areas and hard in others. There is a TON of legacy stuff laying around from 1.0 and 1.1 when they didn't have access to generics. It would have been nice to have them in the first go-around, but it wasn't in the cards. That said, it's beyond outstanding that the 2.0 runtime supports as much extensibility as it does. There's been nothing but patches to the runtime, and the base framework at large, since, oh, mid-2004. They prioritized getting things out like anonymous delegates, generics, etc., which led to such innovations as Linq, functional language support and dynamic runtimes.

The language and framework designers are constantly measuring user requirements up against resource considerations to get you the best bang for the buck. You may see something that looks like a WTF, and sometimes it truly is a questionable decision, but most of the time it's a simple matter of, "this is what we could afford to get out to you when you needed it." I, for one, could not have waited until late-2008 to get this framework.

askheaves
I would expect a FRAMEWORK to be designed top down, not iterativey. Iterative development works when you control the usage of an API, not when you have to support tons of legacy applications.
Sklivvz
It probably was designed top down, but maintenance over a decade is iterative.
bruceatk
A: 

I vote Collections too. And not only the old one, but the new ones too.

Why IEnumerable hasn't got a "Foreach" Extension method? I know that I can write all by myself, but I want to know WHY.

volothamp
+6  A: 

I don't have enough reputation yet to answer volothamp directly, but the .ForEach() thing bothered me at first too. However, I realized that a .ForEach() on an IEnumerable would be incredibly redundant. It's kind of the reason foreach works in the first place. Adding an IEnumerable.ForEach() would just be a second way of doing a foreach loop, but this time with perhaps a performance hit, and with no significant savings in text or clarity.

Really worth anybody's time to implement, test and document if the only thing it accomplishes is helping people shoot themselves in the foot?

askheaves
Well, not every language has a 'foreach' operator.
TraumaPony
I have to disagree. Having a ForEach method means you can reduce your code to a one-liner.
Jonathan Allen
.ForEach() doesn't have the closure problem that foreach suffers from as discussed at http://stackoverflow.com/questions/512166/c-the-foreach-identifier-and-closures
Chris Shouts
+8  A: 

Not that this is any excuse for the framework, but if you've ever worked with MFC, you'd feel that the .net framework was a masterpiece.

+3  A: 

Not including a reference to System.Configuration in WinForm/Console apps by default always made me say, "WTF..."

Giovanni Galbo
why so? a default project doesn't include configuration files, and someone might not even add then. The reference is added automatically when you add a config file. This is the right way to do it.
Pop Catalin
+3  A: 

My WTF is why is there no IsNumeric() function on the string class. VB had it, so why doesn't it exist in C#? I must say though, that with extension methods, I've created an extension to the string class called IsNumeric, so that does kind of solve the problem, but still, WTF?

BFree
Deciding whether a string is a number or not is a pretty complicated issue. I can understand their leaving off a simple IsNumeric method in order to force programmers to address the complexity.
MusiGenesis
Also, my guess is that most people would use IsNumeric to determine whether or not they can parse the string as a number, and that's better done in the TryParse method of the specific numeric type.
MusiGenesis
It doesn't make sense to bloat the string class with non-string methods. Why not "IsGuid" "IsDayOfTheWeek" or "IsProperName" or "IsColorName" methods too? There are TryParse() methods for int and float etc, which serve the same function.
Wedge
+4  A: 

Too much of the documentation is perfunctory and not helpful, especially examples. No help on what the property means, or why one would want to change the value. This sort of thing:

Widget.UserValue This property is the User Value of the Widget.

Example:

Widget.UserValue = true ;

SeaDrive
+8  A: 

Another huge WTF is .Net Framework versioning scheme. I never understand why they renamed WinFX to .Net 3.0. Calling latest version 3.5 completes the confusion for most people.

Imho 3.0 should be 2.5 and 3.5 should be 3.0 (or 2.10 if someone want to respect version of clr). Or they can flollow Ubuntu versioning scheme.

Jakub Šturc
To add to that: .net 1.x and 2.0 are two separate and incompatible runtimes, whereas .net 3.0 and 3.5 are still .net 2.0 and some extra classes.
Michael Stum
.NET 3.5 SP1 is even more WTF-ier than plain old 3.5
Andrei Rinea
+11  A: 

For a great discussion on the poor naming of core .Net classes you mention in the question I would recommend Microsoft's Framework Design Guidelines book. There is a lot of information in that book written by members of the Microsoft .Net team. They discuss how poorly they named items like the *Reader classes, and what they learned from their mistakes. I was impressed at how candid the comments were from various developers inside Microsoft.

Besides being a great postmortem on .Net 1.0, the book discusses naming conventions, design guidelines, etc, for ongoing development of .Net. This is one of my favorite tech books.

There is also a second edition coming out November 3, 2008.

Jason Jackson
+4  A: 
  1. System.Data.SqlCient.SqlException does not have a public constructor, so it makes simulating database failures when mocking difficult.
  2. System.Data.Linq.DataContext does not implement an interface, so it makes mocking LINQ to SQL far more difficult.
jalbert
I had the same problem. The way I got around it was to instantiate SqlExpection through Reflection. It's pain because you have to instantiate 2-3 other objects as well but it is doable.
Richard Nienaber
+5  A: 

Don't forget about these things:

  • Websites. Why did they even bother? They are much slower to compile. They compile down to a "random" number of dlls named in the worst possible way (random names). You need to publish sites in order to deploy them (why?). Pages and classes don't have namespaces by default... and all this when there was a much, much better option, web applications. Fortunately they re-introduced web apps later on, otherwise I would have dropped .Net for any serious work.

  • Object Datasource. Object maybe, but not strongly typed. Everything is a string internally. Sometimes it stops working altogether. Just avoid.

  • Various things in membership. Password can be stored in clear. Or encrypted. Or hashed. And this preference can be changed on a live site. So you end up having half of your passwords hashed and the other half encrypted... in the same database column, IIRC.

  • Again on membership: you have users and profiles. Of course you can't create a user and a profile at the same time (like User u = new User(...); u.Profile = new Profile(...); u.Save();). You have to have 2 database calls. Without a transaction (OK you can use a distributed transaction, but...)

Sklivvz
+1  A: 

No EventArgs<> in 2.0+, I have to write my own each time.

It's not that its hard to write, but... wtf? Especially since they do have EventHandler<>.

Adam Lassek
How exactly do you imagine a generic `EventArgs` class should work?!
Konrad Rudolph
I just want to be able to pass my own arbitrary data during an event. An example of this can be found here: http://codebetter.com/blogs/jean-paul_boodhoo/archive/2007/07/11/leveraging-the-eventhandler-lt-t-gt-delegate-more-effectively.aspx
Adam Lassek
Generic event args are fine for prototypes, but suck for future proofing. You can add properties to concrete classes without breaking old code.
Jonathan Allen
+3  A: 

The DateTime class is causing me major headaches. The fact that for half the year DateTime.Now == DateTime.UtcNow (in the UK anyway...) means that our code gets peppered with code where the two formats are jumbled up, which means when summer rolls around we get problems. I'd like them to be separated into two classes, UtcDateTime and LocalDateTime which would have a TimeZone property.

mcintyre321
A: 

I second DateTime. It would be solved in 3.5 with DateTimeOffset, but DateTimeOffset not generated in the XSD.exe, not are you able to use in XmlSerialization attribute.

I toss in that in .Net 2.0, Nullable was added, and DbNull... If you say it's an improvement on java, then why again did we not use objects.

david valentine
A: 

Generally, this is why it's important to do usability studies of your APIs/Object Models.

.NET classes' usability has improved since 1.1, mainly because of deliberate efforts to make them so.

Tatiana Racheva
A: 

Once upon a time, the fact that System.Uri was a MarshallByRefObject was a huge WTF, but it's been fixed now.

Doug McClean
A: 

a) Lack of visual inheritance with generics in Windows Forms. This makes typeful view - classes a pain in the youknowwhat.

Must have been some intern developing this feature.

b) Necessity to pay a fantasy price to be able to use Code Contracts correctly. I mean... seriously?

stormianrootsolver
+1  A: 
  • Another one for the lame collections. Why the hell was't there a Set<T> class from the start?

  • Why does ReadOnlyCollection<T> inherit off IList<T>? It's meant to be a collection, not a list!

  • ArraySegment doesn't implement any enumerator. Which means you can't use foreach on one.

  • BitArray is non-generic (even in .NET 4), and the eumerator only returns an object, not a bool. Which means that when you're enumerating through one, every single bit value will be boxed and then unboxed.

  • Why does Enum.ToObject() not return an Enum instead of an object? And why does it not have a TryParse method?

  • None of the object GetSomething(Type t, ...) methods scattered around have had T GetSomething<T>(...) versions added, which would remove such nastiness as

    MyEnum e = (MyEnum)Enum.Parse(typeof(MyEnum), value)
    

    and replace it with

    MyEnum e = Enum.Parse<T>(value);
    

    especially for the service componentmodel stuff...

I could go on...

thecoop