C# (.NET) Design Flaws

C#.Net

C# Problem Overview


What are some of the biggest design flaws in C# or the .NET Framework in general?

Example: there is no non-nullable string type and you have to check for DBNull when fetching values from an IDataReader.

C# Solutions


Solution 1 - C#

  • the Reset() method on IEnumerator<T> was a mistake (for iterator blocks, the language spec even demands that this throws an exception)
  • the reflection methods that return arrays were, in Eric's view, a mistake
  • array covariance was and remains an oddity
    • Update: C# 4.0 with .NET 4.0 added covariant/contravariance support to generic interfaces (like IEnumerable<out T> and Func<in T, out TResult>, but not concrete types (like List<T>).
  • ApplicationException rather fell out of favor - was that a mistake?
  • synchronized collections - a nice idea, but not necessarily useful in reality: you usually need to synchronize multiple operations (Contains, then Add), so a collection that synchronizes distinct operations isn't all that useful
    • Update: The System.Collections.Concurrent types, with TryAdd, GetOrAdd, TryRemove, etc were added in .NET Framework 4.0 - though methods that accept a factory delegate do not guarantee the factory will only be invoked once per key.
  • more use could have been made of the using/lock pattern - perhaps allowing them to share a re-usable (extensible?) syntax; you can simulate this by returning IDisposable and using using, but it could have been clearer
  • iterator blocks : no simple way of checking arguments ahead-of-time (rather than lazily). Sure, you can write two chained methods, but that is ugly
  • simpler immutability would be nice; C# 4.0 helps a bit, but not quite enough
  • no "this ref-type parameter cannot be null" support - although contracts (in 4.0) help with this somewhat. But syntax like Foo(SqlConnection! connection) (that injects a null-check / throw) would be nice (contrast to int? etc)
  • lack of support of operators and non-default constructors with generics; C# 4.0 solves this a bit with dynamic, or you can enable it like this
  • the iterator variable being declared outside the while in the foreach expansion, meaning that anon-methods/lambdas capture the single variable, rather than one per iteration (painful with threading/async/etc)

Solution 2 - C#

TextWriter is a base class of StreamWriter. wtf?

That always confuses me to the extreme.

Solution 3 - C#

A small C# pet peev - constructors use the C++/Java syntax of having the constructor be the same name as the class.

New() or ctor() would have been much nicer.

And sure, tools such as coderush make this less of an issue for renaming classes, but from a readability POV, New() provides great clarity.

Solution 4 - C#

I agree emphatically with this post (for those poo-pooing the lack of ToString, there is a debugger attribute to provide a custom format for your class).

On top of the above list, I would also add the following reasonable requests:

  1. non-nullable reference types as a complement to nullable value types,
  2. allow overriding a struct's empty constructor,
  3. allow generic type constraints to specify sealed classes,
  4. I agree with another poster here that requested arbitrary constructor signatures when used as constraints, ie. where T : new(string), or where T : new(string, int)
  5. I also agree with another poster here about fixing events, both for empty event lists and in the concurrent setting (though the latter is tricky),
  6. operators should be defined as extension methods, and not as static methods of the class (or not just as static methods at least),
  7. allow static properties and methods for interfaces (Java has this, but C# does not),
  8. allow event initialization in object initializers (only fields and properties are currently allowed),
  9. why is the "object initializer" syntax only usable when creating an object? Why not make it available at any time, ie. var e = new Foo(); e { Bar = baz };
  10. fix quadratic enumerable behaviour,
  11. all collections should have immutable snapshots for iteration (ie. mutating the collection should not invalidate the iterator),
  12. tuples are easy to add, but an efficient closed algebraic type like "Either<T>" is not, so I'd love some way to declare a closed algebraic type and enforce exhaustive pattern matching on it (basically first-class support for the visitor pattern, but far more efficient); so just take enums, extend them with exhaustive pattern matching support, and don't allow invalid cases,
  13. I'd love support for pattern matching in general, but at the very least for object type testing; I also kinda like the switch syntax proposed in another post here,
  14. I agree with another post that the System.IO classes, like Stream, are somewhat poorly designed; any interface that requires some implementations to throw NotSupportedException is a bad design,
  15. IList should be much simpler than it is; in fact, this may be true for many of the concrete collection interfaces, like ICollection,
  16. too many methods throw exceptions, like IDictionary for instance,
  17. I would prefer a form of checked exceptions better than that available in Java (see the research on type and effect systems for how this can be done),
  18. fix various annoying corner cases in generic method overload resolution; for instance, try providing two overloaded extension methods, one that operates on reference types, and the other on nullable struct types, and see how your type inference likes that,
  19. provide a way to safely reflect on field and member names for interfaces like INotifyPropertyChanged, that take the field name as a string; you can do this by using an extension method that takes a lambda with a MemberExpression, ie. () => Foo, but that's not very efficient, * Update: C# 6.0 added the nameof() operator for single member names, but it doesn't work in generics (nameof(T) == "T" instead of the actual type-argument's name: you still need to do typeof(T).Name)) - nor does it allow you to get a "path" string, e.g. nameof(this.ComplexProperty.Value) == "Value" limiting its possible applications.
  20. allow operators in interfaces, and make all core number types implement IArithmetic; other useful shared operator interfaces are possible as well,
  21. make it harder to mutate object fields/properties, or at the very least, allow annotating immutable fields and make the type checker enforce it (just treat it as getter-only property fer chrissakes, it's not hard!); in fact, unify fields and properties in a more sensible way since there's no point in having both; C# 3.0's automatic properties are a first step in this direction, but they don't go far enough, * Update: While C# had the readonly keyword, and C# 6.0 added read-only auto-properties, though it isn't as stringent as true language support for immutable types and values.
  22. simplify declaring constructors; I like F#'s approach, but the other post here that requires simply "new" instead of the class name is better at least,

That's enough for now I suppose. These are all irritations I've run into in the past week. I could probably go on for hours if I really put my mind to it. C# 4.0 is already adding named, optional and default arguments, which I emphatically approve of.

Now for one unreasonable request:

  1. it'd be really, really nice if C#/CLR could support type constructor polymorphism, ie. generics over generics,

Pretty please? :-)

Solution 5 - C#

I don't understand that you can't do

where T : new(U)

So you declare that generic type T has a non-default constructor.

edit:

I want to do this:

public class A 
{
    public A(string text) 
    {

    }
}


public class Gen<T> where T : new(string text) 
{

}

Solution 6 - C#

  1. I'm not a big fan of the Stream, StringWriter, StringReader, TextReader, TextWriter classes...it's just not intuitive what is what.
  2. IEnumerable.Reset throwing an exception for iterators. I have some third party components which always call reset when databound, requires me to cast to a list first to use these.
  3. Xml Serializer should have serialized IDictionary elements
  4. I totally forgot about the HttpWebRequest & FTP API what a pain in my....(thanks for the comment Nicholas to remind me of this:-)

Edit
5. Another annoyance of mine is how System.Reflection.BindingFlags, has different uses depending on the method your using. In FindFields for example what does CreateInstance or SetField mean? This is a case where they have overloaded the meaning behind this enumeration which is confusing.

Solution 7 - C#

I'm really surprised that I'm the first to mention this one:

ADO.NET typed data sets don't expose nullable columns as properties of nullable types. You should be able to write this:

int? i = myRec.Field;
myRec.Field = null;

Instead, you have to write this, which is just stupid:

int? i = (int?)myRec.IsFieldNull() ? (int?)null : myRec.Field;
myRec.SetFieldNull();

This was annoying in .NET 2.0, and it's even more annoying now that you have to use jiggery-pokery like the above in your nice neat LINQ queries.

It's also annoying that the generated Add<TableName>Row method is similarly insensible to the notion of nullable types. All the more so since the generated TableAdapter methods aren't.

There's not a lot in .NET that makes me feel like the dev team said "Okay, boys, we're close enough - ship it!" But this sure does.

Solution 8 - C#

I don't know that I'd go as far as to say it's a design flaw, but it would be really nice if you could infer a lambda expression in the same way you can in VB:

VB:

Dim a = Function(x) x * (x - 1)

C#

It would be nice if could do this:

var a = x => x * (x - 1);

Instead of having to do this:

Func<int, int> a = x => x * (x - 1);

I realise it's not much longer, but in Code Golf every character counts damnit! Don't they take that into account when they design these programming languages? :)

Solution 9 - C#

  1. The System.Object class:
  • Equals and GetHashCode - not all classes are comparable or hashable, should be moved to an interface. IEquatable or IComparable (or similar) comes to mind.

  • ToString - not all classes can be converted to a string, should be moved to an interface. IFormattable (or similar) comes to mind.

  1. The ICollection.SyncRoot property:
  • Promotes poor design, an external lock is almost always more useful.
  1. Generics should have been there from the beginning:
  • The System.Collections namespace contains a lot of more or less obsolete classes and interfaces.

Solution 10 - C#

One of the things that irritates me is the Predicate<T> != Func<T, bool> paradox. They're both delegates of type T -> bool and yet they're not assignment compatible.

Solution 11 - C#

Some people (ISVs) wish that you could compile it to machine code at build time, and link it, in order to create a native executable which doesn't need the dotNet run-time.

Solution 12 - C#

We know so much about the right OO techniques. Decoupling, programming by contract, avoiding improper inheritance, appropriate use of exceptions, open/closed principal, Liskov substitutability, and so on. Any yet, the .Net frameworks do not employ best practices.

To me the single biggest flaw in the design of .Net is not standing on the shoulders of giants; promoting less than ideal programming paradigms to the masses of programmers that use their frameworks.

If MS paid attention to this, the software engineering world could have made great leaps in terms of quality, stability and scalability in this decade, but alas, it seems to be regressing.

Solution 13 - C#

I don't like the C# switch statement.

I would like something like this

switch (a) {
  1    : do_something;
  2    : do_something_else;
  3,4  : do_something_different;
  else : do_something_weird; 
}

So no more breaks (easy to forget) and the possibility to comma-separate different values.

Solution 14 - C#

Events in C#, where you have to explicit check for listeners. Wasn't that the point with events, to broadcast to whoever happen to be there? Even if there aren't any?

Solution 15 - C#

The awful (and quite invisible to most people) O(N^2) behaviour of nested/recursive iterators.

I'm quite gutted that they know about it, know how to fix it but it is not viewed as having sufficient priority to merit inclusion.

I work with tree like structures all the time and have to correct otherwise smart people's code when they inadvertently introduce highly expensive operations in this way.

The beauty of "yield foreach' is that the simpler, easier syntax encourages correct, performant code. This is the "pit of success" that I think they should aspire to before adding new features for long term success of the platform.

Solution 16 - C#

Some classes implement interfaces but they don't implement many of the methods of that interface, for example Array implements IList but 4 out of 9 methods throw NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members.aspx

Solution 17 - C#

Static members and nested types in interfaces.

This is particularly useful when an interface member has a parameter of a type that is specific to the interface (e.g. an enum). It would be nice to nest the enum type in the interface type.

Solution 18 - C#

The terribly dangerous default nature of events. The fact that you can call an event and be in an inconsistent state due to subscribers being removed is just horrible. See Jon Skeet's and Eric Lippert's excellent articles for more reading on the subject.

Solution 19 - C#

  • null everywhere.

  • const nowhere.

  • APIs are inconsistent, e.g. mutating an array returns void but appending to a StringBuffer returns the same mutable StringBuffer.

  • Collection interfaces are incompatible with immutable data structures, e.g. Add in System.Collections.Generic.IList<_> cannot return a result.

  • No structural typing so you write System.Windows.Media.Effects.SamplingMode.Bilinear instead of just Bilinear.

  • Mutable IEnumerator interface implemented by classes when it should be an immutable struct.

  • Equality and comparison are a mess: you've got System.IComparable and Equals but then you've also got System.IComparable<_>, System.IEquatable, System.Collections.IComparer, System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable, System.Collections.Generic.IComparer and System.Collections.Generic.IEqualityComparer.

  • Tuples should be structs but structs unnecessarily inhibit tail call elimination so one of the most common and fundamental data types will allocate unnecessarily and destroy scalable parallelism.

Solution 20 - C#

0 moonlighting as enum

peculiarities of enum: http://blogs.msdn.com/abhinaba/archive/2007/01/09/more-peculiarites-of-enum.aspx

as illustrated by this good example: http://plus.kaist.ac.kr/~shoh/postgresql/Npgsql/apidocs/Npgsql.NpgsqlParameterCollection.Add_overload_3.html

my suggestion, put the "@" sign to good use:

instead of:

if ((myVar & MyEnumName.ColorRed) != 0)

use this:

if ((myVar & MyEnumName.ColorRed) != @0)

Solution 21 - C#

To add to the long list of good points made by others already:

  • DateTime.Now == DateTime.Now in most, but not all cases.

  • String which is immutable has a bunch of options for construction and manipulation, but StringBuilder (which is mutable) doesn't.

  • Monitor.Enter and Monitor.Exit should have been instance methods, so instead of newing a specific object for locking, you could new a Monitor and lock on that.

  • Destructors should never have been named destructors. The ECMA spec calls them finalizers, which is much less confusing for the C++ crowd, but the language specification still refers to them as destructors.

Solution 22 - C#

The way we use properties irritates me sometimes. I like to think of them as the equivalent of Java's getFoo() and setFoo() methods. But they are not.

If the Property Usage Guidelines state that properties should be able to be set in any order so serialization can work, then they're useless for setter-time validation. If you come from a background where you like to prevent an object from allowing itself to ever get into an invalid state, then properties aren't your solution. Sometimes I fail to see just how they are better than public members, since we're so limited in what kinds of things we're supposed to do in properties.

To that end, I've always kind of wished (this is mostly thinking out loud here, I just kind of wish I could do something like this) that I could extend the property syntax somehow. Imagine something like this:


private string password;




public string Password
{
// Called when being set by a deserializer or a persistence
// framework
deserialize
{
// I could put some backward-compat hacks in here. Like
// weak passwords are grandfathered in without blowing up
this.password = value;
}
get
{
if (Thread.CurrentPrincipal.IsInRole("Administrator"))
{
return this.password;
}
else
{
throw new PermissionException();
}
}
set
{
if (MeetsPasswordRequirements(value))
{
throw new BlahException();
}
this.password = value;
}
serialize
{
return this.password;
}
}

public string Password { // Called when being set by a deserializer or a persistence // framework deserialize { // I could put some backward-compat hacks in here. Like // weak passwords are grandfathered in without blowing up this.password = value; } get { if (Thread.CurrentPrincipal.IsInRole("Administrator")) { return this.password; } else { throw new PermissionException(); } } set { if (MeetsPasswordRequirements(value)) { throw new BlahException(); } this.password = value; } serialize { return this.password; } }

I'm not sure if that's useful or what it accessing those would look like. But I just wish that I could do more with properties and really treat them like get and set methods.

Solution 23 - C#

Extension methods are nice but they're an ugly way to solve problems that could have been solved cleaner with real mixins (look at ruby to see what I'm talking about), on the subject of mixins. A really nice way to add them to the language would have been to allow generics to be used for inheritance. This allows you to extend existing classes in a nice object oriented way:

public class MyMixin<T> : T
{
    // etc...
}

this can be used like this to extend a string for example:

var newMixin = new MyMixin<string>();

It's far more powerful than extension methods because it allows you to override methods, for example to wrap them allowing AOP-like functionality inside the language.

Sorry for the rant :-)

Solution 24 - C#

The .Parameters.Add() method on the SqlCommand in V1 of the framework was horribly designed -- one of the overloads would basically not work if you passed in a parameter with a value (int) of 0 -- this led to them creating the .Parameters.AddWithValue() method on the SqlCommand class.

Solution 25 - C#

  • Be able to invoke an extension method on null variable is arguable e.g.

    object a=null; a.MyExtMethod(); // this is callable, assume somewhere it has defined MyExtMethod

    It could be handy but it is ambiguous on null reference exception topics.

  • One naming 'flaw'. 'C' of "configuration" in System.configuration.dll should be capitalized.

  • Exception handling. Exception should be forcibly caught or thrown like in Java, the compiler should check it at compilation time. Users should not rely on comments for exceptions info within the target invocation.

Solution 26 - C#

Microsoft won't fix obvious bugs in the framework and won't provide hooks so end users can fix them.

Also, there is no way to binary-patch .NET executables at runtime and no way to specify private versions of .NET framework libraries without binary patching the native libraries (to intercept the load call), and ILDASM is not redistributable so I cannot automate the patch anyway.

Solution 27 - C#

  1. There are no subsets of ICollection<T> and IList<T>; at minimum, a covariant read-only collection interface IListSource<out T> (with an enumerator, indexer and Count) would have been extremely useful.
  2. .NET does not support weak delegates. The workarounds are clumsy at best, and listener-side workarounds are impossible in partial trust (ReflectionPermission is required).
  3. Generic interface unification is forbidden even when it makes sense and cause no problems.
  4. Unlike in C++, covariant return types are not allowed in .NET
  5. It is not possible to bitwise-compare two value types for equality. In a functional "persistent" data structure, I was writing a Transform(Sequence<T>, Func<T,T>) function that needed to quickly determine whether the function returns the same value or a different value. If the function does not modify most/all of its arguments, then the output sequence can share some/all memory from the input sequence. Without the ability to bitwise compare any value type T, a much slower comparison must be used, which hurts performance tremendously.
  6. .NET doesn't seem able to support ad-hoc interfaces (like those offered in Go or Rust) in a performant manner. Such interfaces would have allowed you to cast List<T> to a hypothetical IListSource<U> (where T:U) even though the class doesn't explicitly implement that interface. There are at least three different libraries (written independently) to supply this functionality (with performance drawbacks, of course--if a perfect workaround were possible, it wouldn't be fair to call it a flaw in .NET).
  7. Other performance issues: IEnumerator requires two interface calls per iteration. Plain method pointers (IntPtr-sized open delegates) or value-typed delegates (IntPtr*2) are not possible. Fixed-size arrays (of arbitrary type T) cannot be embedded inside classes. There is no WeakReference<T> (you can easily write your own, but it will use casts internally.)
  8. The fact that identical delegate types are considered incompatible (no implicit conversion) has been a nuisance for me on some occasions (e.g. Predicate<T> vs Func<T,bool>). I often wish we could have structural typing for interfaces and delegates, to achieve looser coupling between components, because in .NET it is not enough for classes in independent DLLs to implement the same interface--they must also share a common reference to a third DLL that defines the interface.
  9. DBNull.Value exists even though null would have served the same purpose equally well.
  10. C# has no ??= operator; you must write variable = variable ?? value. Indeed, there are a few places in C# that needlessly lack symmetry. For example you can write if (x) y(); else z(); (without braces), but you can't write try y(); finally z();.
  11. When creating a thread, it is impossible to cause the child thread to inherit thread-local values from the parent thread. Not only does the BCL not support this, but you can't implement it yourself unless you create all threads manually; even if there were a thread-creation event, .NET can't tell you the "parents" or "children" of a given thread.
  12. The fact that there are two different length attributes for different data types, "Length" and "Count", is a minor nuisance.
  13. I could go on and on forever about the poor design of WPF... and WCF (though quite useful for some scenarios) is also full of warts. In general, the bloatedness, unintuitiveness, and limited documentation of many of the BCL's newer sublibraries makes me reluctant to use them. A lot of the new stuff could have been far simpler, smaller, easier to use and understand, more loosely coupled, better-documented, applicable to more use cases, faster, and/or more strongly typed.
  14. I'm often been bitten by the needless coupling between property getters and setters: In a derived class or derived interface, you can't simply add a setter when the base class or base interface only has a getter; if you override a getter then you are not allowed to define a setter; and you can't define the setter as virtual but the getter as non-virtual.

Solution 28 - C#

One thing that ticked me off in 1.x was when using the System.Xml.XmlValidatingReader, the ValidationEventHandler's ValidationEventArgs doesn't expose the underlying XmlSchemaException (marked internal) which has all the useful info like linenumber and position. Instead you're expected to parse this out of the Message string property or use reflection to dig it out. Not so good when you want to return a more sanitised error to the end user.

Solution 29 - C#

Don't like it that you can't use the values of one enum in another enum, for example:

    enum Colors { white, blue, green, red, black, yellow }

    enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow } 

Solution 30 - C#

Implicitly Typed variables were implemented poorly IMO. I know you should really only use them when working with Linq expressions, but it's annoying that you can't declare them outside of local scope.

From MSDN:

  • var can only be used when a local variable is declared and initialized in the same statement; the variable cannot be initialized to null, or to a method group or an anonymous function.
  • var cannot be used on fields at class scope.
  • Variables declared by using var cannot be used in the initialization expression. In other words, this expression is legal: int i = (i = 20); but this expression produces a compile-time error: var i = (i = 20);
  • Multiple implicitly-typed variables cannot be initialized in the same statement.
  • If a type named var is in scope, then the var keyword will resolve to that type name and will not be treated as part of an implicitly typed local variable declaration.

The reason I think it's a poor implementation is that they call it var, but it's a long way from being a variant. It's really just shorthand syntax for not having to type the fully class name (except when used with Linq)

Solution 31 - C#

The StreamWriter and StreamReader classes (and subclasses) closing the underlying stream on Close() and Dispose(). Can't count how many times I worked around this design choice.

Solution 32 - C#

I honestly have to say that during my years of .NET ( C# ) programming I haven't flaws in the framework design that I've remembered; Meaning that in my case there are probably no flaws that are worth remembering.

However, there is something that I dissliked a couple of years back when Microsoft was releasing XNA, they completely cut of their MDX 2.0-version, which made my games unplayable and not easy to just convert. This is a broader flaw and has nothing to do with the .NET-framework.

The .NET-framework actually follows a lot of Very Good design guidelines developed by a lot of the high end language architectures. So I have to say that im happy about .NET.

But to tell you something that could be better, I'd have to complain about the Generic system, I don't find the Generics for Interfaces such as "where T is MyObj" ( that's not the completely correct syntax. However, this part could have been made much better and clearer.

Imagine having an Interface which 2 different classes are sharing, if you want a Generic method inside that interface, you need to go over some nasty Generics-sytanx. It might just be me wanting to do weird stuff. Only memmorable thing for me though.

Solution 33 - C#

The CLR (and therefore C#) doesn't support Multiple Inheritance and ASP.NET is stuffed with LSP breaks...

Those are my "favorites"...

I could probably find more bugs, but those are the ones I dislikes the most...!! :(

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRauhotzView Question on Stackoverflow
Solution 1 - C#Marc GravellView Answer on Stackoverflow
Solution 2 - C#QuibblesomeView Answer on Stackoverflow
Solution 3 - C#Scott WeinsteinView Answer on Stackoverflow
Solution 4 - C#naaskingView Answer on Stackoverflow
Solution 5 - C#tuinstoelView Answer on Stackoverflow
Solution 6 - C#JoshBerkeView Answer on Stackoverflow
Solution 7 - C#Robert RossneyView Answer on Stackoverflow
Solution 8 - C#BenAlabasterView Answer on Stackoverflow
Solution 9 - C#dalleView Answer on Stackoverflow
Solution 10 - C#Greg BeechView Answer on Stackoverflow
Solution 11 - C#ChrisWView Answer on Stackoverflow
Solution 12 - C#Daniel PaullView Answer on Stackoverflow
Solution 13 - C#tuinstoelView Answer on Stackoverflow
Solution 14 - C#Thomas EydeView Answer on Stackoverflow
Solution 15 - C#ShuggyCoUkView Answer on Stackoverflow
Solution 16 - C#ggf31416View Answer on Stackoverflow
Solution 17 - C#Jay BazuziView Answer on Stackoverflow
Solution 18 - C#jasonhView Answer on Stackoverflow
Solution 19 - C#J DView Answer on Stackoverflow
Solution 20 - C#Michael BuenView Answer on Stackoverflow
Solution 21 - C#Brian RasmussenView Answer on Stackoverflow
Solution 22 - C#Nicholas PiaseckiView Answer on Stackoverflow
Solution 23 - C#MendeltView Answer on Stackoverflow
Solution 24 - C#Dave MarkleView Answer on Stackoverflow
Solution 25 - C#Ray LuView Answer on Stackoverflow
Solution 26 - C#JoshuaView Answer on Stackoverflow
Solution 27 - C#QwertieView Answer on Stackoverflow
Solution 28 - C#KevView Answer on Stackoverflow
Solution 29 - C#tuinstoelView Answer on Stackoverflow
Solution 30 - C#lomaxxView Answer on Stackoverflow
Solution 31 - C#GaussZView Answer on Stackoverflow
Solution 32 - C#Filip EkbergView Answer on Stackoverflow
Solution 33 - C#Thomas HansenView Answer on Stackoverflow