In C#, I started seeing all these magic methods popping up, without being backed up by an interface. Why was this chosen?
Let me explain.
Previously in C#, if an object implemented the IEnumerable
interface, it would automatically be iterable by a foreach
loop. That makes sense to me, since it’s backed up by an interface, and if I were to have my own Iterator
function inside the class being iterated through, I could do that without worrying that it would magically mean something else.
Now, apparently, (not sure when), these interfaces are no longer required. It just needs to have the right naming conversions.
Another example is making any object awaitable by having a method named exactly GetAwaiter
which has a few specific properties.
Why not make an interface like they did with IEnumerable
or INotifyPropertyChanged
to back this “magic” up statically?
More details on what I mean here:
http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/
What are the pros and cons of magic methods, and is there anywhere online where I can find anything on why these decisions were made?
10
In general “magic methods” are used when it’s not possible to create an interface that would work the same way.
When foreach
was first introduced in C# 1.0 (that behavior is certainly nothing recent), it had to use magic methods, because there were no generics. The options basically were:
-
Use the non-generic
IEnumerable
andIEnumerator
that work withobject
s, which means boxing value types. Since iterating something like a list ofint
s should be very fast and certainly shouldn’t create lots of garbage boxed values, this is not a good choice. -
Wait for generics. This would probably mean delaying .Net 1.0 (or at least
foreach
) by more than 3 years. -
Use magic methods.
So, they chose option #3 and it stayed with us for backwards compatibility reasons, even though since .Net 2.0, requiring IEnumerable<T>
would have worked too.
Collection initializers can look different on each collection type. Compare List<T>
:
public void Add(T item)
and Dictionary<TKey, TValue>
:
public void Add(TKey key, TValue value)
You can’t have a single interface that would support only the first form for List<T>
and only the second for Dictionary<TKey, TValue>
.
LINQ methods are usually implemented as extension methods (so that there can be just a single implementation of e.g. LINQ to Object for all types that implement IEnumerable<T>
), which means it’s not possible to use an interface.
For await
, The GetResult()
method can return either void
or some type T
. Again, you can’t have a single interface that can handle both. Though await
is partially interface-based: the awaiter has to implement INotifyCompletion
and can also implement ICriticalNotifyCompletion
.
5