Fun with IL DASM and Duck Typing
The code for my little example looks like this:
public class DuckCollection
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
public class EnumerableCollection : IEnumerable
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
public class ForEacher
{
public void DoForEach()
{
var duckCollection = new DuckCollection();
foreach (var thing1 in duckCollection)
{
}
var enumerableCollection = new EnumerableCollection();
foreach (var thing2 in enumerableCollection)
{
}
}
}
After I compile this code, I’ve opened it up with IL DASM, so I can see what the generated IL is for this code.
.method public hidebysig instance void DoForEach() cil managed
{
// Code size 146 (0x92)
.maxstack 2
.locals init ([0] class DuckTypingILCode.DuckCollection duckCollection,
[1] object thing1,
[2] class DuckTypingILCode.EnumerableCollection enumerableCollection,
[3] object thing2,
[4] class [mscorlib]System.Collections.IEnumerator CS$5$0000,
[5] bool CS$4$0001,
[6] class [mscorlib]System.IDisposable CS$0$0002)
IL_0000: nop
IL_0001: newobj instance void DuckTypingILCode.DuckCollection::.ctor()
IL_0006: stloc.0
IL_0007: nop
IL_0008: ldloc.0
IL_0009: callvirt instance class [mscorlib]System.Collections.IEnumerator DuckTypingILCode.DuckCollection::GetEnumerator()
IL_000e: stloc.s CS$5$0000
.try
{
IL_0010: br.s IL_001c
IL_0012: ldloc.s CS$5$0000
IL_0014: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0019: stloc.1
IL_001a: nop
IL_001b: nop
IL_001c: ldloc.s CS$5$0000
IL_001e: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0023: stloc.s CS$4$0001
IL_0025: ldloc.s CS$4$0001
IL_0027: brtrue.s IL_0012
IL_0029: leave.s IL_0048
} // end .try
finally
{
IL_002b: ldloc.s CS$5$0000
IL_002d: isinst [mscorlib]System.IDisposable
IL_0032: stloc.s CS$0$0002
IL_0034: ldloc.s CS$0$0002
IL_0036: ldnull
IL_0037: ceq
IL_0039: stloc.s CS$4$0001
IL_003b: ldloc.s CS$4$0001
IL_003d: brtrue.s IL_0047
IL_003f: ldloc.s CS$0$0002
IL_0041: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0046: nop
IL_0047: endfinally
} // end handler
IL_0048: nop
IL_0049: newobj instance void DuckTypingILCode.EnumerableCollection::.ctor()
IL_004e: stloc.2
IL_004f: nop
IL_0050: ldloc.2
IL_0051: callvirt instance class [mscorlib]System.Collections.IEnumerator DuckTypingILCode.EnumerableCollection::GetEnumerator()
IL_0056: stloc.s CS$5$0000
.try
{
IL_0058: br.s IL_0064
IL_005a: ldloc.s CS$5$0000
IL_005c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0061: stloc.3
IL_0062: nop
IL_0063: nop
IL_0064: ldloc.s CS$5$0000
IL_0066: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_006b: stloc.s CS$4$0001
IL_006d: ldloc.s CS$4$0001
IL_006f: brtrue.s IL_005a
IL_0071: leave.s IL_0090
} // end .try
finally
{
IL_0073: ldloc.s CS$5$0000
IL_0075: isinst [mscorlib]System.IDisposable
IL_007a: stloc.s CS$0$0002
IL_007c: ldloc.s CS$0$0002
IL_007e: ldnull
IL_007f: ceq
IL_0081: stloc.s CS$4$0001
IL_0083: ldloc.s CS$4$0001
IL_0085: brtrue.s IL_008f
IL_0087: ldloc.s CS$0$0002
IL_0089: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_008e: nop
IL_008f: endfinally
} // end handler
IL_0090: nop
IL_0091: ret
} // end of method ForEacher::DoForEach
Yes, it’s a bit ugly, but you will notice that other than naming, the code is just repeated twice. This is of course what we would expect, since the code should treat it the same regardless. All the foreach loop needed was to be able to get the object from the GetEnumerator method. Once it has that, it just uses the enumerator to do all of the work. This means that either class should work just as well.
This is the repeated line of code that shows up in two places to get the enumerator it will be using for its MoveNext() and GetCurrent() methods.
IL_0009: callvirt instance class [mscorlib]System.Collections.IEnumerator DuckTypingILCode.DuckCollection::GetEnumerator()
IL_0051: callvirt instance class [mscorlib]System.Collections.IEnumerator DuckTypingILCode.EnumerableCollection::GetEnumerator()
Now if you’re really paying attention, you will have looked at the Try-Finally block that is in the code to handle our IDisposables. That’s another neat thing happening behind the scenes, but that’s another blog post entirely.
If you would like to see another cool thing in the .NET Framework, you should check out what else you can do with Null Instance methods if you change those callvirts.
Ball Flow Recap: CodeMash Coding Dojo
The coding dojo at CodeMash 2012 was a blast this year. I ran two events in the dojo: a group exercise doing the Gilded Rose kata and the Ball Flow agile game. I thought the gilded rose kata we did as a group exercise was a great success. We had a good group of people, which was impressive considering that even with floor decals, people had trouble finding the coding dojo.
The coding dojo at codemash is a great place for actually doing things at CodeMash. If you’re tired of sitting there having someone talk at you, head to the coding dojo. It’s a place for writing code, learning, experimenting, and having a great time. We did katas, programming exercises, and some educational games in the dojo this year. If you did not make it to the dojo this year, we hope to see you next year!
Ball FlowThe Ball Flow game is an Agile exercise where a team of people work together in an experiment in self organization. The team will do some estimation, some planning, some retrospectives, and they will be trying to continually improve their process while aspects outside of their control continue to change. The team has to adapt and figure out how to keep working together effectively.
Object of the game:Pass each of the 20 balls to each person in the group.
Rules of the game:- A person may touch a ball more than once, but doing so isn’t ideal.
- Two people may not be touching the ball at the same time.
- You may not pass the ball to your nearest two neighbors
- If the ball touches walls (including floor and ceiling) or any furniture it must start over.
- The person who first picks up the ball must be the last to touch the ball.
Our group started out by figuring that if they created an oblong shape, they could pass across pretty easily. They got in this shape and began passing around. They had two tall people stand back a step so that they could launch the ball over everyone else across the group. Their final pass went behind the backs of one side of the group back to the starting person who removed the ball from the game.
They spent some time planning and adjusting between each iteration. They made sure not to make big changes fearing the catastrophe that could occur. Each time they made modest improvements to their time.
As the game went on we added challenges, in one round we added a large number of pens that needed to be passed around as well.
In the final round we added in bags of potato chips, which were harder to throw across the room. Each time the group still managed to make slight adjustments to accommodate these changes and still slightly improve their times.
The CodeMash 2012 Ball Flow team was awesome! Great job everyone!
Foreach, IEnumerable, IEnumerator, and Duck Typing
During my Software Craftsmanship Precompiler session, I heard one of the students say, “all you need is an IEnumerable to use a foreach loop”. This sparked a bit of fun when I asked Steve Smith, my co-presenter, if that was correct. He confirmed that it was, and I disagreed. Being the scientists that we are, we decided to try it and see what happened. I of course knew that duck typing in C# should allow the Foreach loop to compile without anything having the method required by the IEnumerable interface. This means that we just need a GetEnumerator method.
We wrote the code that did this and it compiled!
Duck typing is awesome, because it allows the language to treat my type the way I want it to because it has the right tools to do the job. The term duck typing comes from the idea that if it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
In the foreach loop example, duck typing assumes that what you have here is able to be enumerated, because it has a method to get the enumerator. If we look at the IEnumerable interface, we can see that this is what we are required to implement.
public class MyCollection : IEnumerable
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
Now that we have our collection written and implementing IEnumerable, we can write a foreach loop that uses our collection.
var myCollection = new MyCollection();
foreach (var thing in myCollection)
{
// Do something with the thing
}
Finally, because of the duck typing in C#, we can remove the interface from the code, but keep the method.
public class MyCollection
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
It still compiles!
One of the many cool things about C# that I love. It’s a fun language, so go experimenting!
CodeMash 2012 Recap
Now that CodeMash is over, it’s about time that I deposited information about my experiences at CodeMash 2012 here.
This was my third time at this event that always offers great sessions, workshops, discussions, fun, and bacon.
I am honored to have again been given the chance to speak at CodeMash. I co-presented the Software Craftsmanship precompiler workshops with Steve Smith for the 3rd year running. This year, we broke our day-long workshop into 2 sections: one beginner session and one intermediate session.
Both sessions turned out really well. We had a lot of good verbal feedback from the attendees during the workshops, we had some of our morning people stick around for the afternoon, and I heard from other people that those who attended enjoyed the workshop. I am really happy with how well it turned out. Thanks everyone who attended the sessions.
Beginning Software CraftsmanshipIn the morning we did a beginner’s workshop that introduced the idea of Software Craftsmanship and what values go along with it. We discussed what people can do to get involved with their communities and realign their focus on building good, high quality software. As part of this, we show the group how they can work on improving their skills as Software Craftsmen through Katas and other programming exercises.
We had between 30 and 40 people attend the morning, beginner workshop, including a cobol programmer. We had the attendees mostly working on the Prime Factors Kata through the day. We started them doing the kata with little direction and asked that they do the work solo and without testing. We then had them do the kata again, but this time use testing to keep them on track designing their applications in a simpler way. In the third time doing the kata, we had everyone work in pairs on the kata to see how far they could get as a team using TDD.
Our main goal with the programming exercises in the morning is to have everyone leave with an understanding of pair programming, TDD, how to use programming exercises to hone their development skills, and we wanted them to leave motivated to work with their local communities to all become better at creating quality software. I believe we succeeded in our endeavor. The students talked about how the testing made the work easier and that pair programming was also much easier.
Intermediate Software CraftsmanshipThe afternoon focused on some of the same ideas as the morning, but the people in the afternoon know about the values of software craftsmanship. We briefly reminded everyone, as I believe should be done. It’s important to remember and discuss why we do what we do. It gives us a chance to remember and reconsider everything we know and believe.
For this workshop, we had more than 50 people show up for the workshop, which meant that some people did not have table space for their laptops.
In the afternoon we did a few different exercises. One of them focusing on green-field development using good design patterns and practices to help reinforce how to use these effectively. The Greed kata is a great place to try out the strategy pattern as well as a few other good patterns. In the exercise, you continue to get more and more scoring rules added. Eventually, you want to get to following the Open/Closed Principle so that you’re not changing the existing code each time.
The other exercise we did is one which starts out with existing code to refactor. In the refactoring exercise we ask you to add a new feature. It then becomes your choice how to do it. You could just hack in an “if” statement. You could also take some time and refactor. Before you refactor, however, it’s usually a good idea to try to get some tests in place. To make things even more like real code, you need to perform a couple of careful refactorings before you can put your first tests in place. You also get to decide how many tests you want to write and how much to refactor when adding this new feature.
Future EventsIf you want to take part in this or a future workshop, I am hoping that CodeMash will invite me back next year to do another great workshop or two. I am also hoping to do a free Software Engineering 101 event again in Cleveland.