<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2232785552580074568</id><updated>2010-09-02T08:34:31.427+03:00</updated><title type='text'>Sapien Coder</title><subtitle type='html'>The thoughts of a mere mortal programmer</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default?orderby=updated'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>19</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-2078415601144125549</id><published>2010-09-02T08:32:00.001+03:00</published><updated>2010-09-02T08:34:31.434+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='giveaway'/><category scheme='http://www.blogger.com/atom/ns#' term='garmin'/><category scheme='http://www.blogger.com/atom/ns#' term='dcrainmaker'/><title type='text'>September Garmin Forerunner 310XT Giveaway</title><content type='html'>DC Rainmaker is giving another Garmin 310XT.&lt;br /&gt;Hurry and sign up:&lt;br /&gt;&lt;a href="http://www.dcrainmaker.com/2010/09/september-garmin-forerunner-310xt.html"&gt;http://www.dcrainmaker.com/2010/09/september-garmin-forerunner-310xt.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Better yet, don't sign up, so I have a better chance of winning :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-2078415601144125549?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/2078415601144125549/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=2078415601144125549&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/2078415601144125549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/2078415601144125549'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2010/09/september-garmin-forerunner-310xt.html' title='September Garmin Forerunner 310XT Giveaway'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-9043304461806540566</id><published>2010-08-05T09:55:00.000+03:00</published><updated>2010-08-05T09:56:57.810+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='giveaway'/><category scheme='http://www.blogger.com/atom/ns#' term='garmin'/><title type='text'>DCRainmaker is Giving Away a Garmin 310XT</title><content type='html'>Check out all the details here:&lt;br /&gt;http://www.dcrainmaker.com/2010/08/garmin-forerunner-310xt-giveaway.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-9043304461806540566?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/9043304461806540566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=9043304461806540566&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/9043304461806540566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/9043304461806540566'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2010/08/dcrainmaker-is-giving-away-garmin-310xt.html' title='DCRainmaker is Giving Away a Garmin 310XT'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-6488464168744675782</id><published>2010-07-02T14:58:00.001+03:00</published><updated>2010-07-02T15:00:15.795+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='giveaway'/><category scheme='http://www.blogger.com/atom/ns#' term='garmin'/><category scheme='http://www.blogger.com/atom/ns#' term='dcrainmaker'/><title type='text'>Another Giveaway from DCRainmaker</title><content type='html'>This month Ray is giving away a Garmin Forerunner 110!&lt;br /&gt;Cool indeed.&lt;br /&gt;check out the details on his &lt;a href="http://www.dcrainmaker.com/2010/07/garmin-forerunner-110-giveaway.html"&gt;blog&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-6488464168744675782?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/6488464168744675782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=6488464168744675782&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/6488464168744675782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/6488464168744675782'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2010/07/another-giveaway-from-dcrainmaker.html' title='Another Giveaway from DCRainmaker'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-2744525991845323045</id><published>2010-06-02T11:34:00.002+03:00</published><updated>2010-06-02T11:35:31.969+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='giveaway'/><category scheme='http://www.blogger.com/atom/ns#' term='fitness'/><title type='text'>DC Rainmaker is giving out a free Wifi Scale</title><content type='html'>Just thought you'd like to know...&lt;br /&gt;Here is a link to the giveaway:&lt;br /&gt;&lt;a href="http://www.dcrainmaker.com/2010/06/withings-wifi-scale-giveaway.html"&gt;http://www.dcrainmaker.com/2010/06/withings-wifi-scale-giveaway.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-2744525991845323045?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/2744525991845323045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=2744525991845323045&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/2744525991845323045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/2744525991845323045'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2010/06/dc-rainmaker-is-giving-out-free-wifi.html' title='DC Rainmaker is giving out a free Wifi Scale'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-7251251696221997233</id><published>2009-06-07T21:30:00.003+03:00</published><updated>2009-07-04T21:17:45.692+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gps'/><category scheme='http://www.blogger.com/atom/ns#' term='garmin'/><category scheme='http://www.blogger.com/atom/ns#' term='navigation'/><category scheme='http://www.blogger.com/atom/ns#' term='forerunner'/><title type='text'>Navigate with the Garmin Forerunner 305</title><content type='html'>A couple of weeks ago, one of my riding buddies invited me to join him on a training ride to prepare for the &lt;a href="http://www.bikeisrael.com/0082_msgen.asp?msid=2,49"&gt;Jerusalem Marathon&lt;/a&gt;. The idea was to ride one lap around the 30 KM course to get to know it before the actual race. Since neither of us ever rode that course before, he asked me if I could add it to my Forerunner 305 GPS and use it to show us the way.&lt;br /&gt;&lt;br /&gt;I love my Garmin FR305. I use it all the time for training. It provides me with lots of information about my runs/rides both during and after workouts. But it has never occurred to me that I could use it for navigation. So I did some research online (there's not much information on the official Garmin site) and found a few tutorials that explain the process. Since I couldn't find anything detailed enough, I thought I'd write my own take of it and share it with my readers. So here goes...&lt;br /&gt;&lt;br /&gt;To get started you'll need to get a hold of a course file. There are many tools and sites that let you create a course file by drawing a route on a digital map and saving it locally, where the most common is Google Maps. In Israel, you can use &lt;a href="http://amudanan.co.il/"&gt;Amud Anan&lt;/a&gt; to draw the course on a topographic terrain map (Google Maps does not provide topographic information for Israel yet) and save it to your computer. You can also download an existing course file from a plethora of sites that store and categorize routs for hiking, running, biking etc. and let users upload and share their own routs. There's &lt;a href="http://www.btrails.com/"&gt;btrail&lt;/a&gt;, &lt;a href="http://www.usatf.org/routes/view.asp?rID=152247"&gt;USATF&lt;/a&gt;, &lt;a href="http://www.mapmyrun.com/"&gt;MapMyRun&lt;/a&gt;, MotionBased &lt;a href="http://trail.motionbased.com/trail/network/digest/view.mb"&gt;Trail Network&lt;/a&gt; just to name a few.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 132px; height: 320px;" src="http://1.bp.blogspot.com/_vkjNVzG9O6o/SiyrpG_ijVI/AAAAAAAACTs/B0IRH5YYzyY/s320/gpsies.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5344835580522040658" /&gt;&lt;br /&gt;&lt;br /&gt;The important thing is to store the file in the correct format for the Forerunner 305 which is a .crs file (remember, this is a course file, not a workout file). There are a ton of different file formats for the different GPS devices and tools, but luckily it's easy to convert from one format to another using &lt;a href="http://www.gpsies.com/convert.do"&gt;this&lt;/a&gt; conversion tool. Just point to your source file, select the format you're interested in (Garmin Course CRS) and hit Convert. If you choose Options, you can also name the course, and add/fix elevation data to your course.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 320px; height: 207px;" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/SiwFuIeB4QI/AAAAAAAACS8/VRAS5_dIb8c/s320/Capture1.JPG" border="0" alt="" id="BLOGGER_PHOTO_ID_5344653147887493378" /&gt;&lt;br /&gt;&lt;br /&gt;Once you have the CRS file stored, open Garmin Training Center, and choose File -&gt; Import -&gt; Courses. Select the course file, and voila, your file is right there under the Courses tab. Now connect your FR305 and click on the "Send to Device" button to transfer the new course to your Garmin Forerunner.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 320px; height: 104px;" src="http://1.bp.blogspot.com/_vkjNVzG9O6o/SiwF2sGcLvI/AAAAAAAACTE/gwzlSD1s6Yk/s320/Capture2.JPG" border="0" alt="" id="BLOGGER_PHOTO_ID_5344653294891183858" /&gt;&lt;br /&gt;&lt;br /&gt;Once the course is on the device, click Mode, then choose Training, Courses and select the course you want to follow. Click Do Course to start. The timer does not actually start until you press the normal Start button.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 163px; height: 103px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/SiwFk3zco8I/AAAAAAAACS0/ZVF6x_IQix8/s320/garmin_forerunner305-page-menu.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5344652988795102146" /&gt;&lt;br /&gt;&lt;br /&gt;In navigation mode, the FR305 provides screens that are unavailable in standard training mode: navigation data fields, map view, elevation graph, compass, etc. You switch between the "special" screens using the Mode button, and the standard screen using the regular up/down arrow keys.&lt;br /&gt;&lt;br /&gt;The most useful screen in my opinion is the map screen. It shows the planned course in a thick line, and the actual traveled track in a thin dotted line, and the current location is shown using a small triangle. This screen makes it very easy to follow the planned route.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 250px; height: 250px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/SiwFTRCWDyI/AAAAAAAACSk/JJLBorRASgo/s320/Garmin305.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5344652686330826530" /&gt;&lt;br /&gt;&lt;br /&gt;One complaint I have about navigating with the FR305 is that it displays a "Off Course" message too often, even when I'm on course or off by a tiny bit. the problem is that the Off Course message stays there until you hit the Enter button which could be difficult in the middle of a hard climb or a fast descent. And while the message is on screen it is hard to see the data behind it.&lt;br /&gt;&lt;br /&gt;Other than that, it's a great way to explore new trails without getting lost.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vkjNVzG9O6o/Sk-cgp8wukI/AAAAAAAACic/1Za-EmfMRYE/s1600-h/030720092984.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 278px; height: 320px;" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/Sk-cgp8wukI/AAAAAAAACic/1Za-EmfMRYE/s320/030720092984.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5354670566797261378" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vkjNVzG9O6o/Sk-cbnJQIhI/AAAAAAAACiU/jz0ZRtVACII/s1600-h/030720092983.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 258px; height: 320px;" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/Sk-cbnJQIhI/AAAAAAAACiU/jz0ZRtVACII/s320/030720092983.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5354670480144998930" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vkjNVzG9O6o/Sk-cWBV0mjI/AAAAAAAACiM/q2UrQrSzSEc/s1600-h/030720092982.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 302px; height: 320px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/Sk-cWBV0mjI/AAAAAAAACiM/q2UrQrSzSEc/s320/030720092982.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5354670384097827378" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-7251251696221997233?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/7251251696221997233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=7251251696221997233&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/7251251696221997233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/7251251696221997233'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2009/06/navigate-with-garmin-forerunner-305.html' title='Navigate with the Garmin Forerunner 305'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vkjNVzG9O6o/SiyrpG_ijVI/AAAAAAAACTs/B0IRH5YYzyY/s72-c/gpsies.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-12474935032219417</id><published>2009-05-23T15:30:00.003+03:00</published><updated>2009-05-30T21:13:31.041+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mountain biking'/><title type='text'>The Evolution of My Mountain Biking</title><content type='html'>&lt;img style="margin: 0px auto 10px; display: block; text-align: center; width: 400px; height: 104px;" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/Sfl0OnB2uQI/AAAAAAAACE4/a8LiuW-2EtY/s400/bike_evolution_sticker.jpg" alt="Evolution" id="BLOGGER_PHOTO_ID_5330419428313446658" border="0"&gt;&lt;br /&gt;&lt;br /&gt;If you've read my &lt;a href="http://www.sapiencoder.com/2009/03/running-geek.html"&gt;previous&lt;/a&gt; post about running gadgets, you've probably assumed that I'm a runner. Well, I am now, but I wasn't always one. My true love has always been mountain biking. More accurately - my current true love is mountain biking. Before that it was &lt;a href="http://www.flickr.com/photos/urini/sets/72157603208129521/"&gt;BMX&lt;/a&gt;, and before that &lt;a href="http://www.flickr.com/photos/urini/sets/72157608563708846/"&gt;offroad dirt bikes&lt;/a&gt;. I guess I just love anything with two wheels and handlebars. I've been riding bikes since forever, and mountain bikes since 1994. I can't remember a time in my life when I didn't own a bike or two. Maybe only during my 4 year military service.&lt;br /&gt;&lt;br /&gt;A few hours ago I came back from an &lt;a href="http://trail.motionbased.com/trail/activity/8283087"&gt;epic ride&lt;/a&gt; with my friends. It's the same group of people I've been riding with for the past three years. The ride was awesome! We headed out to the Jerusalem mountains for some fast cross-country riding with lots of long steep climbs combined with a couple of classic single tracks. When I look at my friends I'm amazed to see how much our riding has changed in the last couple of years. These days most of us are riding steel hard tail bikes with no gears. It's a long leap sideways from the high-tech aluminum long-travel full-suspension bikes we all owned and rode just a few short years back. But I'm moving too fast here, let me start from the beginning.&lt;br /&gt;&lt;br /&gt;The bike that I consider to be my first "real" mountain bike was a rigid 1994 Kona Lava Dome.&lt;img style="margin: 0pt 0pt 10px 10px; float: right; width: 320px; height: 250px;" src="http://1.bp.blogspot.com/_vkjNVzG9O6o/SfbeAHmkivI/AAAAAAAACEY/BlHcFAX96h8/s320/lavadome_s.jpg" alt="Kona Lava Dome" id="BLOGGER_PHOTO_ID_5329691302661753586" border="0"&gt; It was 2 sizes too big, and was very very, well, rigid. But I loved it. It was fast and light, and climbed like there was no tomorrow. It had a beautiful cromoly frame, Grip Shift shifters, cantilever breaks (which were a pain to adjust) and flat, narrow handlebars - SWEET!&lt;img style="margin: 0pt 0pt 10px 10px; float: right; width: 320px; height: 183px;" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/SfmXuR0TICI/AAAAAAAACFY/YkxiPXzGHt4/s320/technion_campus.jpg" alt="Technion" id="BLOGGER_PHOTO_ID_5330458455282229282" border="0"&gt; The reason I bought it (not that I really needed a reason) was to commute to my &lt;a href="http://www.technion.ac.il/"&gt;school&lt;/a&gt; which was located smack in the middle of the Carmel mountains in Haifa. To grab some extra credit I joined the school's mountain biking team, which meant I had to attend the team's weekly early morning rides (rain or shine) to the &lt;a href="http://www.haifa.ac.il/"&gt;Haifa University&lt;/a&gt; which was all the way up the mountain. We took the road going up, and once we got there we chose one of the numerous fire roads that lead back down and just raced down full speed ahead - no single tracks, no technical trails, just full-on speed that, on a rigid bike, would loosen the fillings in your mouth and blur your vision. Most of the time it also meant climbing back from the bottom of the mountain to the Technion's campus which was about half way up. By 8:30 when class started, we'd be back in school all sweaty, smelly and - depending on the weather - soaking wet, but with huge smiles on our faces. This is what got me hooked on mountain biking.&lt;br /&gt;&lt;br /&gt;Around 1998, a little after I graduated form university, I traveled to visit my kid brother in Canada. We went on a roadtrip through the Canadian Rockies. On one stop along the way, I think it was in Lake Luise, my brother and I rented mountain bikes and went for a short ride in the trails around the lake. &lt;img style="margin: 0pt 0pt 10px 10px; float: right; width: 185px; height: 200px;" src="http://3.bp.blogspot.com/_vkjNVzG9O6o/SfmtKubtY3I/AAAAAAAACFw/SQK9qgq4OF0/s200/devinci.gif" alt="DeVinci" id="BLOGGER_PHOTO_ID_5330482033744241522" border="0"&gt;It was the most beautiful bike ride I've done until then - long winding single track in deep green woods that seemed to last forever. Absolutely perfect. When we got back to the store to return the bikes I noticed that they were having a huge end of season sale (it was the end of summer) on all their new bikes. I just couldn't resist, and bought my first full-suspension bike - a &lt;a href="http://www.devinci.com/"&gt;DeVinci&lt;/a&gt; Magma. Or Contact. I forget. What I do remember is that I actually tried out two different bikes: an XC race-ready hard tail, and a more moderate full-suspension FSR style bike. I almost went for the hard tail just because it had an XTR rear derailleur. Plus, full-suspension bikes weren't very common back then, and I was afraid of being an early adopter. What a dope. Luckily I wasn't fooled by the bling of the XTR, and went for the full-suspension bike. Apart from the XT rear derailleur it also had a RockShox Judy 80mm fork, 3.25" of rear wheel travel, V-breaks and a wide Titec riser bar which I had cut by an inch on each side (once again, what a dope). I remember coming home with the new bike and taking it in for a tune-up at the local bike shop, the bike mechanic was so impressed with the suspension, he suggested I sign up for the upcoming downhill race! I'm glad I didn't listen to him... I enjoyed riding the DeVinci very much, but didn't quite know what it was best for. I took it out to the most technical single tracks available at the time (mostly motorbike enduro trails), and was surprised the bike didn't handle them very well. What was I expecting? Anyway, at the time motorbikes started grabbing more and more of my attention, and the poor bike was left alone to collect dust on the porch while I rode the same trails on a 2 stroke KTM.&lt;br /&gt;&lt;br /&gt;Motorbikes came and went, and one day in late 2003 I noticed a new trend: everybody around me was talking about, or buying all mountain bikes. So, I decided to join the long travel revolution (what can I say, I'm a trendy guy), and bought a &lt;a href="http://www.bikes.com/"&gt;Rocky Mountain&lt;/a&gt; Slayer 70 with 5 long, plush inches of front and rear travel.&lt;img style="float:right; margin:0 0 10px 10px; width: 200px; height: 200px;" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/Sfn1HuvqlsI/AAAAAAAACGo/4kDS0Hcd_Rc/s200/DSC02482.JPG" border="0" alt="Me with my Slayer 70" id="BLOGGER_PHOTO_ID_5330561147125536450" /&gt; Finally, I thought, a bike that can handle anything I throw at it. It was the bike that got me riding mountain bikes again. Biking in Israel was taking off like crazy, new trails were popping up everywhere, and everyone I knew was riding bikes. The new trails were getting more and more technical with rock drops, jumps and steep downhills. There were even a few trails that had wooden bridges, ladders and jumps built along them a-la North Shore. In most cases I handled the technical elements quite well with my Slayer, but I was starting to feel a bit inadequate. Everywhere I looked 6 and 7 inch bikes were roaming the trails, riders wearing full-face helmets and body armor were sharing the same trails I was, taking crazy lines down the mountain sides. It was getting just too tempting. So once again, in late 2007 I said goodbye to my Slayer and bought a new bike: a &lt;a href="http://www.d4m.com/axion/devinci/9685_an.html"&gt;DeVinci Frantik&lt;/a&gt;.&lt;img style="float:right; margin:0 0 10px 10px; width: 320px; height: 194px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/SgBCqfBCg5I/AAAAAAAACJc/NLDmUdxb3Fs/s320/frantik3.jpg" border="0" alt="DeVinci Frantik 3" id="BLOGGER_PHOTO_ID_5332335256454464402" /&gt; This time a full-fledged freeride bike. It had a coil spring, a beefy 180mm single crown fork, 1.5" head tube and it weighed 18KG. A freaking beast - and I loved it. This time there were no excuses, I rode everywhere, down the gnarliest trails and the craziest drops. And I did it all with relative ease. This bike was awesome, it made me a better rider by providing stability and security in the most difficult and unpredicted places. It showed me that I can actually do and dare more than I ever thought I could. But all this goodness came at a price - it was all becoming too easy. More and more I was starting to feel that it wasn't me but the bike taking these crazy lines and speeding down these fast trails. Plus, I also learned that Israel is not BC, and Merav is not exactly Whistler. I spent way too much time pedaling up the trails on an 18KG bike that's specifically designed to do one thing: go fast downhill. I realized that on 80% of the time, this bike is not exactly the right tool for the job. Yes, it was fun, but it was also punishing. I hated the climbs, I hated missing out on epic rides with my friends, and I was quickly getting out of shape. Which takes us back to the beginning of my story, and today's ride with my friends.&lt;br /&gt;&lt;br /&gt;So, in November of 2008 I bought a steel hard tail bike with 29" wheels and no gears, the Niner Bikes &lt;a href="http://www.ninerbikes.com/fly.aspx?layout=bikes&amp;taxid=96"&gt;SIR9&lt;/a&gt;. In a way this took me back 15 years to my old steel Kona, but after a few rides with the SIR I've realized it's the best bike I've ever owned. I feel like all the improvements in bike technology over the years have made me numb. These long travel bikes are fun to ride, no doubt, but they isolate the rider from the terrain and from the sensations that come from being 100% connected with your bike. I now know that there's no such thing as a do-it-all bike, and that you need the right tool for the job to get the most out of it, but this bike &lt;span style="font-weight: bold;"&gt;IS&lt;/span&gt; the right tool for the job 85% of the time, it's all up to the rider to make it happen and not give up. I guess I went full circle from steel, rigid bikes to full-suspension aluminum beasts and back, and all I can say is steel is real baby, steel is real!&lt;br /&gt;&lt;br /&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; width: 384px; height: 400px; border: 3px solid #c3c3c3;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/Shfrb5fRviI/AAAAAAAACMs/ZlL6KFIaCJQ/s400/2777441200951135226.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5338994747792014882" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-12474935032219417?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/12474935032219417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=12474935032219417&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/12474935032219417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/12474935032219417'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2009/05/evolution-of-my-mountain-biking.html' title='The Evolution of My Mountain Biking'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vkjNVzG9O6o/Sfl0OnB2uQI/AAAAAAAACE4/a8LiuW-2EtY/s72-c/bike_evolution_sticker.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-8643576161261083885</id><published>2009-03-24T19:45:00.000+02:00</published><updated>2009-03-24T19:45:20.032+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gadgets'/><category scheme='http://www.blogger.com/atom/ns#' term='running'/><title type='text'>The Running Geek</title><content type='html'>Aside from being a full time developer and a gadget fanatic I'm also an enthusiastic runner. Running helps me stay fit - that's obvious - but it also gives me an opportunity to expand my geeky horizons.&lt;br /&gt;&lt;br /&gt;There are many aspects of running that can make a geek truly excited. You have your selection of motion control-super comfy-arch supporting-long lasting shoes, the dizzying array of light weight-sweat wicking-all weather-quick drying clothes, and of course the energy bars, gels, drinks and powders that fill up aisles and aisles in your local organic food co-op. But my two favorite running gadgets are, by far, the running watch and the music player. I'll start with the watch.&lt;br /&gt;&lt;br /&gt;When I first started running I bought the cheapest stopwatch I could find in my neighborhood drug store. It cost me 10 bucks, and it worked perfectly. I ran the same 5k course twice a week, and all I really needed was to know how long it took each time.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 237px; height: 250px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/ScFKxAGyvbI/AAAAAAAAB40/jHflHT0pdSA/s320/TriaxC8.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5314611240976432562" /&gt;&lt;br /&gt;After a while I decided I also wanted to track my heart rate. Many runners claim it makes training more efficient, but to me it wasn't about that. I just thought it was cool to be able to see your heart rate change in real time while running. So I went out and got the Nike C8 running watch with the HR monitor.&lt;br /&gt;&lt;br /&gt;I enjoyed the C8 for a while, but there was a problem. It had no memory. It didn't keep a log of my runs, so I couldn't compare runs, track my progress or view my running history. All the data it displayed, disappeared as soon as I reset the timer, and that just didn't cut it. Oh, and there was one more issue. Over the years I started to run in more diverse locations. Tracking my running distance was becoming more important to me as well.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 230px; height: 230px;" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/ScFLTbtDbbI/AAAAAAAAB48/jW0YpZY7aLA/s320/RS200sd.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5314611832500219314" /&gt;&lt;br /&gt;So I decided to spend the cash and buy the Polar RS200 with the accessory foot pod. This was a huge technological leap forward. The polar collected information about the time, distance, pace, heart rate, and more, showed it in real time, and  - my favorite feature - allowed me to upload the data to the &lt;a href="http://www.polarpersonaltrainer.com"&gt;Polar Personal Trainer&lt;/a&gt; site for logging. I would sit for hours analyzing each run. I enjoying comparing recent runs with older ones, examining my progress. I generated reports showing the total distance I ran in the last year, and the aggregated number of calories I've burnt since I bought the watch. It was great. It was fun. But, you guessed it, there were some problems. First of all, the foot pod. The main issue was its accuracy. The foot pod could be very accurate as long as you calibrate it properly. And by properly I mean a different setting for each shoe, track and style of running. If you calibrated it for a road run, it would be off if you ran on a trail. If you calibrate it during your 18K slow pace run, it would be off in the 5K dash. Plus, it was bulky and inconvenient, and the batteries ran out just when I most needed it - in the middle of a race, of course.&lt;br /&gt;&lt;br /&gt;Then there's Polar's "special" data transfer protocol. It goes like this: You place the watch close the your PC microphone and hit the send button (oh, and you have to make sure that the PC software is in listen mode first). At that point the watch starts emitting these horrible sounds just like an old 14,400 baud US Robotics modems sounded. About 50% of the time, the connection would fail mid-way through the transfer, and you'd have to start all over again.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px; width: 320px; height: 186px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/ScFR-gzMPYI/AAAAAAAAB5E/W-OMBFW78oY/s320/Forerunner305.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5314619169672281474" /&gt;&lt;br /&gt;These reasons, plus the unbelievably low price ($160 at Amazon) convinced me to purchase the coolest running watch (if you can even call it a watch) I ever had - the Garmin Forerunner 305. I'll never forget the first time I took it our for a run - it felt so good to loose the foot pod. Here's what I like about it: it's light weight yet feels very solid on the wrist. It has a large display that is very easy to read while running. It hooks up to the PC by USB, it even has its own little cradle. And it stores so much data about your runs it will knock your socks off. Since it has a GPS unit built right into the device, there's no foot pod, no external GPS, no calibration, and best of all, it works great during other outdoor activities like biking, skiing, or horse back riding (you can't swim with it though - it's not water proof).&lt;br /&gt;&lt;br /&gt;Garmin offers a desktop client (&lt;a href="http://www8.garmin.com/products/trainingcenter/"&gt;Garmin Training Center&lt;/a&gt;) that lets you analyze your runs and store your training history. But I personally prefer the online options they offer. Garmin offers several online applications each for a different purpose or device. There's &lt;a href="http://home.trainingpeaks.com/"&gt;Training Peaks&lt;/a&gt;, &lt;a href="http://www.newleaffitness.com/"&gt;New Leaf&lt;/a&gt;, &lt;a href="http://trail.motionbased.com/trail/user/urinizan"&gt;Motion Based&lt;/a&gt; and the most recent addition to the Garmin web arsenal &lt;a href="http://connect.garmin.com/"&gt;Garmin Connect&lt;/a&gt;. I currently use &lt;a href="http://trail.motionbased.com/trail/user/urinizan"&gt;Motion Based&lt;/a&gt; which is decent - it provides plenty of information regarding the activity - but the UI is kind of old and confusing, and for some reason they charge $12 a month to view all your activities (with the free service gives you just see the last 10). Hopefully, it won't be long until they finally complete the &lt;a href="http://forums.motionbased.com/smf/index.php?topic=11431.0"&gt;migration&lt;/a&gt; to Garmin Connect.&lt;br /&gt;&lt;br /&gt;Music players are the second piece of running gear I love to geek out on. Music goes exceptionally well with running (see &lt;a href="http://nikeplus.nike.com"&gt;Nike+&lt;/a&gt;). I love to run outdoors, and music helps me stay motivated and strong when I go out on long runs al by myself.&lt;br /&gt;&lt;br /&gt;The first player I owned that's worth mentioning is the 3rd Gen iPod I got in late 2003.&lt;img style="float:right; margin:0 0 10px 10px;width: 120px; height: 200px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/ScfpcYmPLEI/AAAAAAAAB7g/_bcyhZP2xsk/s200/iPod3G.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5316474558982597698" /&gt; It had a 40GB hard disk - enough to store my entire music collection - but it was relatively big and uncomfortable to run with. I even bought an arm band for it, but it just didn't work well. After it got stolen, I bought a 60GB 4G Photo iPod, &lt;img style="float:right; margin:0 0 10px 10px;width: 122px; height: 200px;" src="http://3.bp.blogspot.com/_vkjNVzG9O6o/Scfpjx5GUtI/AAAAAAAAB7o/RSt76YS8cn0/s200/iPodPhoto4G.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5316474686031680210" /&gt;but again, it wasn't ideal for running. It often skipped, it was heavy, and not very resistant to hits and bangs. It died less than a year after I bought it.&lt;br /&gt;&lt;br /&gt;That's when I bought my first running specific player - the Rio Forge.&lt;img style="float:right; margin:0 0 10px 10px;width: 130px; height: 130px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/ScfpsgV-lDI/AAAAAAAAB7w/o2xwzJVK3AQ/s320/RioForge.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5316474835939791922" /&gt; The Forge was small and curvy - designed to hold in your hand while running. It featured a built-in stopwatch, FM receiver and a memory card expansion slot. But it was terribly slow to  start-up, the navigation controls were horrible to use while running, and the arm band was not comfortable at all.&lt;br /&gt;&lt;br /&gt;Luckily I got a 1st Gen 1GB iPod Nano as a Christmas present from work.&lt;img style="float:right; margin:0 0 10px 10px;width: 183px; height: 200px;" src="http://1.bp.blogspot.com/_vkjNVzG9O6o/Scfp3-mAO1I/AAAAAAAAB74/fGEbPO_5H-s/s200/iPodNano.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5316475033038633810" /&gt; It was an amazing player. Tiny - compared to anything else at the time - slick, and real easy to use. Navigation was easy, start up was instantanious, music syncing was a breeze with iTunes, 1GB was just enough to store all my favorite running songs, and the Nano was small enough to hold in my hand while running. It's the player I used the longest so far.&lt;br /&gt;&lt;br /&gt;Recently I gave up my Nano for a new player - the Sansa Clip 4GB. I was looking for a really tiny player that will clip on to my running shorts. I considered getting the iPod Shuffle, but the Sansa Clip turned out to be a much better deal.&lt;img style="float:right; margin:0 0 10px 10px;width: 320px; height: 266px;" src="http://2.bp.blogspot.com/_vkjNVzG9O6o/Scfp_F48gmI/AAAAAAAAB8A/K_D4CJuf3ag/s320/SansaClip.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5316475155256214114" /&gt; It cost about half of what a 2GB Shuffle would have cost me, it has a screen so I can see what's playing and choose a playlist, it has an FM tuner, a voice recorder, excellent sound quality, and comfortable in-ear headphones. I love the Clip - it's so small and light I barely notice it when I run.&lt;br /&gt;&lt;br /&gt;So much for running geekyness. Next time I'll write about my other obsession - mountain biking.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-8643576161261083885?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/8643576161261083885/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=8643576161261083885&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/8643576161261083885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/8643576161261083885'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2009/03/running-geek.html' title='The Running Geek'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vkjNVzG9O6o/ScFKxAGyvbI/AAAAAAAAB40/jHflHT0pdSA/s72-c/TriaxC8.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-7529223735076567483</id><published>2008-10-02T19:00:00.006+03:00</published><updated>2008-10-03T08:56:12.187+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='radiohead'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Music Video Without Cameras</title><content type='html'>&lt;div&gt;While browsing Google's &lt;a href="http://www.google.com/tenthbirthday/#start"&gt;10th birhtday site&lt;/a&gt; I ran across a cool video that cought my eye. It gave a behind the scenes look at the creation of Radiohead's &lt;a href="http://code.google.com/creative/radiohead/"&gt;"House of Cards"&lt;/a&gt; music video.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;img style="float:right; margin:0 0 10px 10px;" src="http://1.bp.blogspot.com/_vkjNVzG9O6o/SOTiVMJudfI/AAAAAAAAAjA/CLqe9FZzMn0/s320/3dmodel.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5252571919087990258" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;It turns out the entire video was shot without using a single camera or strobe. Instead, they used two experimental technologies, Geometric Informatics and Velodyne LIDAR that capture 3D models in real time. The former was used to create the close-up shots, while the latter was used for the landscapes.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Google also published a data &lt;a href="http://code.google.com/creative/radiohead/viewer.html"&gt;viewer&lt;/a&gt; that lets you manipulate the 3D model to get a sense of the data captured, its properties, and size. The really cool part is that you can move Thom Yorke's head around while the video is playing! As a data visualization geek, this sort of stuff really gets me going.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-7529223735076567483?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/7529223735076567483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=7529223735076567483&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/7529223735076567483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/7529223735076567483'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/10/music-video-without-cameras.html' title='Music Video Without Cameras'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vkjNVzG9O6o/SOTiVMJudfI/AAAAAAAAAjA/CLqe9FZzMn0/s72-c/3dmodel.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-2780769551566714689</id><published>2008-09-01T18:30:00.007+03:00</published><updated>2008-09-22T14:13:46.216+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='delegates'/><category scheme='http://www.blogger.com/atom/ns#' term='async'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='apm'/><title type='text'>Using Delegates to Implement APM</title><content type='html'>In my &lt;a href="http://www.sapiencoder.com/2008/08/evolution-of-delegates.html"&gt;previous&lt;/a&gt; post I described how delegates have evolved over the various versions of the .NET framework. In this post I want to elaborate and describe how to use delegates to implement asynchronous programming.&lt;br /&gt;&lt;br /&gt;Many of today's PCs have multiple processors/cores in them, and there's a definite trend among processor makers to gradually increase that number in future models. In order to utilize the increasing number of cores, computer programs need to parallelize their execution, and assign processor intensive tasks to dedicated threads. Writing robust multi-threaded software is not easy. Threads need to be synchronized, data locked, and dead-locks are difficult to avoid. This increases the challenge for developers who need to write robust and efficient multi-threaded code.&lt;br /&gt;&lt;br /&gt;To help programmers out, Microsoft introduced the Asynchronous Programming Model which simplifies multi-threaded programming. They have implemented it themselves in many classes throughout the .NET framework such as the FileStream, Socket, WebRequest, SqlCommand, etc. All of these classes offer asynchronous method calls along-side the standard synchronous versions. The async method name always starts with BeginXxx, and offers a corresponding EndXxx. For example, the SqlCommand class offers an ExecuteReader method, plus an async version of the method - BeginExecuteReader. To comply with the APM it also offers an EndExecuteReader method.&lt;br /&gt;&lt;br /&gt;All delegates implement this pattern out-of-the-box. The all offer the standard Invoke method to execute the target method, and also offer the BeginInvoke and EndInvoke methods to execute the target method asynchronously.&lt;br /&gt;&lt;br /&gt;This is the main reasons &lt;a href="http://www.sapiencoder.com/2008/08/evolution-of-delegates.html"&gt;I love delegates&lt;/a&gt; so much.&lt;br /&gt;&lt;br /&gt;Let's get down to it. One of the complexities of multi-threaded programming is figuring out when an async process completed its work. This is where the APM offers a great deal of help. There are three techniques to find out when a BeginInvoke is done: wait, poll and callback. I'll use a sample to describe the three techniques.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class PrimeCalc&lt;br /&gt;{&lt;br /&gt; public int GetNextPrime(int num)&lt;br /&gt; {&lt;br /&gt;    int p = num + 1;&lt;br /&gt;    while(true)&lt;br /&gt;    {&lt;br /&gt;        if(IsPrime(p))&lt;br /&gt;        {&lt;br /&gt;             break;&lt;br /&gt;        }&lt;br /&gt;        p++;&lt;br /&gt;    }&lt;br /&gt;    return p;&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The PrimeCalc class has a GetNextPrime method that calculates the next prime number after the number passed in to the method. The class uses some internal methods to do the calculation (I'll add the entire source code at the end of the post) and returns the next prime number it finds. We'll use this method to simulate a compute-intensive task.&lt;br /&gt;&lt;br /&gt;Since our target method takes an int as a parameter and returns an int as a return value, we could use the &lt;code&gt;Func&lt;&lt;int,int&gt;&lt;/int,int&gt;&lt;/code&gt; as our delegate:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;PrimeCalc calc = new PrimeCalc();&lt;br /&gt;Func&amp;lt;int, int&amp;gt; del = calc.GetNextPrime;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Wait Until Completed Technique&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void WaitUntilComplete(int num)&lt;br /&gt;{&lt;br /&gt;   PrimeCalc calc = new PrimeCalc();&lt;br /&gt;   Func&amp;lt;int, int&amp;gt; del = calc.GetNextPrime;&lt;br /&gt;   IAsyncResult result = del.BeginInvoke(num, null, null);&lt;br /&gt;&lt;br /&gt;   // Do some other work here&lt;br /&gt;&lt;br /&gt;   // Suspend this thread until the async operation completes&lt;br /&gt;   int prime = del.EndInvoke(result);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is not a very efficient way of using the APM. If the compute-intensive task takes longer than the "other" work that is being done in the mean time, the thread will be suspended and wait for the delegate to return.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Polling Technique&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void Poll(int num)&lt;br /&gt;{&lt;br /&gt;   PrimeCalc calc = new PrimeCalc();&lt;br /&gt;   Func&amp;lt;int, int&amp;gt; del = calc.GetNextPrime;&lt;br /&gt;   IAsyncResult result = del.BeginInvoke(num, null, null);&lt;br /&gt;&lt;br /&gt;   while (!result.IsCompleted)&lt;br /&gt;   {&lt;br /&gt;       // Do some other work here&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   // Get the result from the async operation&lt;br /&gt;   int prime = del.EndInvoke(result);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This option is also not very efficient because the while loop that runs until the async operation completes would consumes CPU resources even after the "other" work that needs to be done is complete.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Callback Technique&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;This is by far my favorite rendezvous technique of all, it is a bit more complex to implement, but offers greater flexibility and control, and is more efficient in terms of resource management.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void Callback(int num)&lt;br /&gt;{&lt;br /&gt;   DateTime start = DateTime.Now;&lt;br /&gt;   Console.WriteLine("Starting async calculation at: {0}", start);&lt;br /&gt;&lt;br /&gt;   PrimeCalc calc = new PrimeCalc();&lt;br /&gt;   Func&amp;lt;int, int&amp;gt; del = calc.GetNextPrime;&lt;br /&gt;   del.BeginInvoke(num, CalcCompleted, start);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void CalcCompleted(IAsyncResult result)&lt;br /&gt;{&lt;br /&gt;   DateTime start = (DateTime)result.AsyncState;&lt;br /&gt;&lt;br /&gt;   Func&amp;lt;int, int&amp;gt; del = (Func&amp;lt;int, int&amp;gt;)((AsyncResult)result).AsyncDelegate;&lt;br /&gt;   int prime = del.EndInvoke(result);&lt;br /&gt;&lt;br /&gt;   DateTime end = DateTime.Now;&lt;br /&gt;   TimeSpan length = end.Subtract(start);&lt;br /&gt;   Console.WriteLine("Completed async calculation at: {0}", end);&lt;br /&gt;   Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The basic idea here is that the delegate calls the callback method when it completes. The signature of the callback method should match the AsyncCallback delegate signature:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;private void CalcCompleted(IAsyncResult result)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This time, when we call the BeginInvoke method on our delegate, we pass in the callback method as the second parameter, and the third parameter could be any state object we would like to pass in to the EndInvoke method. In this sample I passed in the start DateTime of the async opertaion.&lt;br /&gt;&lt;br /&gt;In the callback method you should get a reference to the delegate instance (by casting the result param to the appropriate type), and use it to call EndInvoke. It is important to call EndInvoke even if your method does not return a value. This prevents reasource leaks.&lt;br /&gt;&lt;br /&gt;You get a reference to the state object you passed in the BeginInvoke 3rd parameter by casting the result's AsyncState property to the appropriate type.&lt;br /&gt;&lt;br /&gt;Here is the entire PrimeCalc class source code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;namespace AsyncProgrammingModel&lt;br /&gt;{&lt;br /&gt;class PrimeCalc&lt;br /&gt;{&lt;br /&gt; private List&amp;lt;int&amp;gt; primes;&lt;br /&gt;&lt;br /&gt; public PrimeCalc()&lt;br /&gt; {&lt;br /&gt;  primes = new ListList&amp;lt;int&amp;gt;();&lt;br /&gt;  primes.Add(2);&lt;br /&gt;  primes.Add(3);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public int GetNextPrime(int num)&lt;br /&gt; {&lt;br /&gt; &lt;br /&gt;  int p = num + 1;&lt;br /&gt;  while (true)&lt;br /&gt;  {&lt;br /&gt;   // Create a list of all prime numbers up to p.&lt;br /&gt;   AddPrimesToList(p);&lt;br /&gt;   if (IsPrime(p))&lt;br /&gt;   {&lt;br /&gt;    break;&lt;br /&gt;   }&lt;br /&gt;   p++;&lt;br /&gt;  }&lt;br /&gt;  return p;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void AddPrimesToList(int numberToTest)&lt;br /&gt; {&lt;br /&gt;  int n = primes[primes.Count - 1] + 2;&lt;br /&gt;  while (n &amp;lt; numberToTest)&lt;br /&gt;  {&lt;br /&gt;   if (IsPrime(n))&lt;br /&gt;   {&lt;br /&gt;    primes.Add(n);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   // Skip even numbers.&lt;br /&gt;   n += 2;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private bool IsPrime(int n)&lt;br /&gt; {&lt;br /&gt;  bool foundDivisor = false;&lt;br /&gt;  bool exceedsSquareRoot = false;&lt;br /&gt;&lt;br /&gt;  int i = 0;&lt;br /&gt;  int divisor = 0;&lt;br /&gt;&lt;br /&gt;  // Stop the search if:&lt;br /&gt;  //     there are no more primes in the list,&lt;br /&gt;  //     there is a divisor of n in the list, or&lt;br /&gt;  //     there is a prime that is larger than the square root of n.&lt;br /&gt;  while ((i &amp;lt; primes.Count) &amp;amp;&amp;amp; !foundDivisor &amp;amp;&amp;amp; !exceedsSquareRoot)&lt;br /&gt;  {&lt;br /&gt;   // The divisor variable will be the smallest&lt;br /&gt;   // prime number not yet tried.&lt;br /&gt;   divisor = primes[i++];&lt;br /&gt;&lt;br /&gt;   // Determine whether the divisor is greater than the square root of n.&lt;br /&gt;   if (divisor * divisor &gt; n)&lt;br /&gt;   {&lt;br /&gt;    exceedsSquareRoot = true;&lt;br /&gt;   }&lt;br /&gt;   // Determine whether the divisor is a factor of n.&lt;br /&gt;   else if (n % divisor == 0)&lt;br /&gt;   {&lt;br /&gt;    foundDivisor = true;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return !foundDivisor;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-2780769551566714689?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/2780769551566714689/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=2780769551566714689&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/2780769551566714689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/2780769551566714689'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/09/using-delegates-to-implement-apm.html' title='Using Delegates to Implement APM'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-1322497018817845989</id><published>2008-09-03T19:30:00.003+03:00</published><updated>2008-09-22T14:12:58.057+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lambda'/><category scheme='http://www.blogger.com/atom/ns#' term='delegates'/><category scheme='http://www.blogger.com/atom/ns#' term='async'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='apm'/><title type='text'>The APM, Anonymous Methods and Lambda Expressions</title><content type='html'>In this post I would like to continue from where I left of in my &lt;a href="http://www.sapiencoder.com/2008/09/using-delegates-to-implement-apm.html"&gt;previous&lt;/a&gt; post about the Asynchronous Programming Model, and show how the APM could be more concise using Anonymous Methods and Lambda Expressions.&lt;br /&gt;&lt;br /&gt;Using "classic" delegates the code to implement the APM looks like this (as shown in greater details in my previous post):&lt;br /&gt;&lt;pre&gt;public void ClassicAsync(int num)&lt;br /&gt;{&lt;br /&gt; DateTime start = DateTime.Now;&lt;br /&gt; Console.WriteLine("Starting async calculation at: {0}", start);&lt;br /&gt;&lt;br /&gt; PrimeCalc calc = new PrimeCalc();&lt;br /&gt; Func&amp;lt;int, int&amp;gt; del = calc.GetNextPrime;&lt;br /&gt; del.BeginInvoke(num, CalcCompleted, start);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void CalcCompleted(IAsyncResult result)&lt;br /&gt;{&lt;br /&gt; DateTime start = (DateTime)result.AsyncState;&lt;br /&gt;&lt;br /&gt; Func&amp;lt;int, int&amp;gt; del = (Func&amp;lt;int, int&amp;gt;)((AsyncResult)result).AsyncDelegate;&lt;br /&gt; int prime = del.EndInvoke(result);&lt;br /&gt;&lt;br /&gt; DateTime end = DateTime.Now;&lt;br /&gt; TimeSpan length = end.Subtract(start);&lt;br /&gt; Console.WriteLine("Completed async calculation at: {0}", end);&lt;br /&gt; Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With an anonymous method we can write the callback method inline like this:&lt;br /&gt;&lt;pre&gt;public void AnonymousAsync(int num)&lt;br /&gt;{&lt;br /&gt; DateTime start = DateTime.Now;&lt;br /&gt; Console.WriteLine("Starting anonymous async calculation at: {0}", start);&lt;br /&gt;&lt;br /&gt; PrimeCalc calc = new PrimeCalc();&lt;br /&gt; Func&amp;lt;int, int&amp;gt; del = calc.GetNextPrime;&lt;br /&gt;&lt;br /&gt; del.BeginInvoke(num, delegate(IAsyncResult result)&lt;br /&gt; {&lt;br /&gt;     int prime = del.EndInvoke(result);&lt;br /&gt;     Console.WriteLine(prime);&lt;br /&gt;&lt;br /&gt;     DateTime end = DateTime.Now;&lt;br /&gt;     TimeSpan length = end.Subtract(start);&lt;br /&gt;     Console.WriteLine("Completed anonymous async calculation at: {0}", end);&lt;br /&gt;     Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);&lt;br /&gt; }, null);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And, finally, using a lambda expression the implementation is even more concise:&lt;br /&gt;&lt;pre&gt;public void LambdaAsync(int num)&lt;br /&gt;{&lt;br /&gt; DateTime start = DateTime.Now;&lt;br /&gt; Console.WriteLine("Starting lambda async calculation at: {0}", start);&lt;br /&gt;&lt;br /&gt; PrimeCalc calc = new PrimeCalc();&lt;br /&gt; Func&amp;lt;int, int&amp;gt; del = calc.GetNextPrime;&lt;br /&gt; del.BeginInvoke(num, result =&gt;&lt;br /&gt; {&lt;br /&gt;     int prime = del.EndInvoke(result);&lt;br /&gt;     Console.WriteLine(prime);&lt;br /&gt;&lt;br /&gt;     DateTime end = DateTime.Now;&lt;br /&gt;     TimeSpan length = end.Subtract(start);&lt;br /&gt;     Console.WriteLine("Completed lambda async calculation at: {0}", end);&lt;br /&gt;     Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);&lt;br /&gt; }, null);&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-1322497018817845989?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/1322497018817845989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=1322497018817845989&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/1322497018817845989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/1322497018817845989'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/09/apm-anonymous-methods-and-lambda.html' title='The APM, Anonymous Methods and Lambda Expressions'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-4678231103049133081</id><published>2008-09-06T10:00:00.001+03:00</published><updated>2008-09-22T14:11:21.842+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ad'/><category scheme='http://www.blogger.com/atom/ns#' term='seinfeld'/><category scheme='http://www.blogger.com/atom/ns#' term='microsoft'/><title type='text'>Microsoft Thinks Different</title><content type='html'>I just saw the first &lt;a href="http://www.youtube.com/watch?v=afR5J7eskno"&gt;Microsoft Windows ad featuring Bill Gates and Jerry Seinfeld&lt;/a&gt;. This ad campaign is Microsoft's answer to Apple's &lt;a href="http://www.apple.com/getamac/ads/"&gt;Mac vs. PC&lt;/a&gt; ads which have been going unchallenged for over two years.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I don't know if the ad is working or not, it's probably too soon to tell, but it's definitely been a long while since a Microsoft ad campaign made such a buzz. There is a lot of talk about it in the blogosphere and even in &lt;a href="http://www.nytimes.com/2008/09/06/technology/06soft.html"&gt;mainstream&lt;/a&gt; media.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;According to the &lt;a href="http://online.wsj.com/article/SB121928939429159525.html"&gt;Wall Street Journal&lt;/a&gt;, Microsoft is spending $300M on this ad campaign (Jerry's cut is $10M), so it better work. After the annoying and the what-are-you-trying-to-prove? that-everyone-thinks-Vista-sucks! campaign a.k.a.  "&lt;a href="http://www.mojaveexperiment.com/"&gt;Mojave Experiment"&lt;/a&gt; the expectations are running high.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's ironic that when you search for "Seinfeld ad" in YouTube, the first result is a Mac ad :-)&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/vNDEwsIGJKI&amp;amp;hl=en&amp;amp;fs=1"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;embed src="http://www.youtube.com/v/vNDEwsIGJKI&amp;amp;hl=en&amp;amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-4678231103049133081?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/4678231103049133081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=4678231103049133081&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/4678231103049133081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/4678231103049133081'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/09/microsoft-thinks-different.html' title='Microsoft Thinks Different'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-4953157255243358831</id><published>2008-08-29T14:00:00.004+03:00</published><updated>2008-09-02T12:52:51.906+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lambda'/><category scheme='http://www.blogger.com/atom/ns#' term='delegates'/><category scheme='http://www.blogger.com/atom/ns#' term='async'/><category scheme='http://www.blogger.com/atom/ns#' term='action'/><category scheme='http://www.blogger.com/atom/ns#' term='anonymous'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='func'/><title type='text'>The Evolution of Delegates</title><content type='html'>&lt;p&gt;In this post I'll discuss how delegates have evolved along with the evolution of .NET and C#. Specifically, how the introduction of anonymous methods and lambda expression helped make delegates more concise and useful.&lt;/p&gt;&lt;p&gt;This post is the first in a series of "Back-to-Basic" posts where I'll discuss basic .NET concepts and show how they have progressed with each new release of the .NET Framework.&lt;/p&gt;&lt;p&gt;I love delegates. If I had to come up with a list of my top 5 .NET features, delegates would definitely be high up on the list.&lt;/p&gt;&lt;p&gt;A Delegate, as defined by &lt;a href="http://msdn.microsoft.com/en-us/library/ms173171.aspx"&gt;MSDN&lt;/a&gt; is:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;blockquote&gt;"A type that defines a method signature, and can be associated with any method with a compatible signature. You can invoke (or call) the method through the delegate. Delegates are used to pass methods as arguments to other methods."&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Delegates are first class citizens in the .NET framework and the C# language, and one of the fundamental types in the framework. They are used throughout the framework and enable important features such as events and asynchronous method calls.&lt;/p&gt;&lt;p&gt;In the old days, if we wanted a delegate that takes one parameter and returns void we had to declare one like this:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;delegate&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DoWorkDelegate(&lt;span class="kwrd"&gt;string&lt;/span&gt; data);&lt;/pre&gt;&lt;p&gt;Then we had to implement a method that conforms to the signature of the delegate:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;p&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DoSomeWork(&lt;span class="kwrd"&gt;string&lt;/span&gt; s)&lt;br /&gt;{&lt;br /&gt; // Do some work here&lt;br /&gt; Console.WriteLine(s);&lt;br /&gt;}&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;And finally, instantiate the delegate in our code:&lt;/p&gt;&lt;pre class="csharpcode"&gt;DoWorkDelegate del = &lt;span class="kwrd"&gt;new&lt;/span&gt; DoWorkDelegate(DoSomeWork);&lt;/pre&gt;&lt;p&gt;In .NET 2.0 we could use a simplified syntax to instantiate the delegate:&lt;/p&gt;&lt;pre class="csharpcode"&gt;DoWorkDelegate del = DoSomeWork;&lt;/pre&gt;&lt;p&gt;The introduction of Generics in .NET 2.0 introduces a generic delegate &lt;a href="http://msdn.microsoft.com/en-us/library/system.action.aspx"&gt;Action&lt;/a&gt;. Action encapsulates a delegate that returns void and accepts 0 to 4 parameters (there are actually 5 generic Action types defined: Action, Action&amp;lt;T&amp;gt;, Action&amp;lt;T1, T2&amp;gt;, Action&amp;lt;T1, T2, T3&amp;gt;, Action&amp;lt;T1, T2, T3, T4&amp;gt;). So in the above example, we could have saved us the trouble of defining our own custom delegate "DoWorkDelegate" and instead used this:&lt;/p&gt;&lt;pre class="csharpcode"&gt;Action&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; del = DoSomeWork;&lt;/pre&gt;&lt;p&gt;.NET 2.0 also introduced the concept of &lt;a href="http://msdn.microsoft.com/en-us/library/0yw3tz5k.aspx"&gt;Anonymous Methods&lt;/a&gt;. This lets us avoid defining the "DoSomeWrok" method altogether and write it inline with the definition of the delegate:&lt;/p&gt;&lt;pre class="csharpcode"&gt;Action&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; del = &lt;span class="kwrd"&gt;delegate&lt;/span&gt;(&lt;span class="kwrd"&gt;string&lt;/span&gt; s)&lt;br /&gt;{&lt;br /&gt; Console.WriteLine(s);&lt;br /&gt;};&lt;/pre&gt;&lt;p&gt;Finally, C# 3.0 introduced &lt;a href="http://msdn.microsoft.com/en-us/library/bb397687.aspx"&gt;Lambda Expressions&lt;/a&gt;, which allow us to define the method inline like this:&lt;/p&gt;&lt;pre class="csharpcode"&gt;Action&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; del = s =&amp;gt;&lt;br /&gt;{&lt;br /&gt; Console.WriteLine(s);&lt;br /&gt;};&lt;/pre&gt;&lt;p&gt;One more delegate worth mentioning is the &lt;a href="http://msdn.microsoft.com/en-us/library/bb534960.aspx"&gt;Func&lt;/a&gt; delegate. Func was introduced in .NET 3.5 and just like its older brother,  the Action, it encapsulates a delegate, but unlike Action Func encapsulates a delegate that takes 0 to 4 and returns a value. For example:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;// Using Func we can replace this:&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;delegate&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; DoWorkDelegate(&lt;span class="kwrd"&gt;string&lt;/span&gt; data);&lt;br /&gt;&lt;br /&gt;&lt;span class="rem"&gt;// with this:&lt;/span&gt;&lt;br /&gt;Func&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;int&lt;/span&gt;&amp;gt; del;&lt;/pre&gt;&lt;p&gt;In all the above samples (except for the last one) we end up with a "del" object. We could then use the "del" instance anywhere in our code to execute the "DoSomeWork" method. We could even pass "del" as an argument to other methods or objects that could also execute the same method:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;// This is the verbose way:&lt;/span&gt;&lt;br /&gt;del.Invoke(&lt;span class="str"&gt;"Do some work"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class="rem"&gt;// This is a bit more concise&lt;/span&gt;&lt;br /&gt;del(&lt;span class="str"&gt;"Do some work"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class="rem"&gt;// This executes the method asynchronously&lt;/span&gt;&lt;br /&gt;del.BeginInvoke(&lt;span class="str"&gt;"Do some work"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;/pre&gt;&lt;p&gt;Next time on SapienCoder I will describe &lt;a href="http://www.sapiencoder.com/2008/09/using-delegates-to-implement-apm.html"&gt;how to use delegates to implement Asynchronous programming&lt;/a&gt; patterns.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-4953157255243358831?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/4953157255243358831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=4953157255243358831&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/4953157255243358831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/4953157255243358831'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/08/evolution-of-delegates.html' title='The Evolution of Delegates'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-9107601234875663527</id><published>2008-08-26T20:00:00.002+03:00</published><updated>2008-08-26T20:19:42.331+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='captcha'/><category scheme='http://www.blogger.com/atom/ns#' term='crowd-sourcing'/><title type='text'>Will reCAPTCHA Save Humanity?</title><content type='html'>&lt;p&gt;Probably not, but it will make us more productive during the tedious process of proving our humanhood to some random web server.&lt;/p&gt;  &lt;p&gt;Anyone who's using the internet these days for anything more involved than reading news knows those annoying sets of garbled characters that appear at the end of checkout and registration pages. &lt;/p&gt;  &lt;p&gt;&lt;img height="60" alt="captcha" src="http://lh4.ggpht.com/uri.nizan/SLQ7KtuZ8iI/AAAAAAAAAgE/bxSc2ied5ck/captcha%5B9%5D.gif?imgmax=800" width="290" /&gt; &lt;/p&gt;  &lt;p&gt;Those are called CAPTCHAs &lt;span id="lblProfile"&gt;(an acronym for &amp;quot;completely automated public Turing test to tell computers and humans apart&amp;quot;)&lt;/span&gt; and they are designed to test that we, the users of the web site are in fact human. Now, I don't disagree with the motivation behind CAPTCHAs. Today's Web is infested with crawling bots and other malicious agents running around causing all kinds of mayhem. But, it just seems like a terrible waste of time for something that aught to be straight forward (how many times have you mistyped a CAPTCHA and had to enter it over and over again until the server acknowledged your humanhood?).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Luis_von_Ahn"&gt;Luis von Ahn&lt;/a&gt;, the inventor of the CAPTCHA, said in an &lt;a href="http://www.npr.org/templates/story/story.php?storyId=93605988"&gt;interview&lt;/a&gt; recently that by his estimates people spend an average of 10 seconds solving one of those CAPTCHA puzzles. Multiply that by the number of CAPTCHAs solved daily (aprox. 200 million) and you come to the staggering number of approximately 500,000 hours per day world wide. Astonishing when you think about it in these terms.&lt;/p&gt;  &lt;p&gt;That's exactly why he came up with the brilliant idea of the &lt;a href="http://recaptcha.net/"&gt;reCAPTCHA&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;There is a growing number of libraries and archives who are working on digitizing their entire collections. The process involves scanning the printed document (book, newspaper, magazine, historical document, etc.) to an image file, and then running a software called OCR (for Optical Character Recognition) that tries to recognize the words in the scanned image and turns them into a searchable text document. It turns out that the OCR software can't &amp;quot;read&amp;quot; every document with 100% accuracy. That's where the power of crowd-sourcing comes in. &lt;img style="margin: 5px" height="178" alt="recaptcha" src="http://lh3.ggpht.com/uri.nizan/SLQ7LIR-JwI/AAAAAAAAAgI/ypCniezUIQE/recaptcha%5B7%5D.jpg?imgmax=800" width="343" align="right" /&gt; ReCAPTCHA uses the words that computers can't decipher with OCR software (therefore can't be &amp;quot;read&amp;quot; by malicious robots) and displays them to humans. Each reCAPTCHA actually consists of two words, one was successfully recognized by the computer and the other wasn't. Each image is also shown to several people to verify the accuracy of the translation. If they agree on the translation the transcription is considered accurate and will be added to the text it originally came from. Currently more than 40,000 web sites world wide are using reCAPTCHA technology including some you might have heard of like &lt;a href="http://www.ticketmaster.com/"&gt;Ticketmaster&lt;/a&gt;, &lt;a href="http://www.facebook.com/"&gt;Facebook&lt;/a&gt; and &lt;a href="http://www.craigslist.org/"&gt;CraigsList&lt;/a&gt;. There are plug-ins for WordPress, Joomla, Drupal and many other popular web applications, as well as APIs for PHP, ASP.NET, Java, Perl, Ruby etc. The implementation is simple and there are lots of resources &lt;a href="http://recaptcha.net/resources.html"&gt;available&lt;/a&gt; for site developers.&lt;/p&gt;  &lt;p&gt;Next time you run into those squiggly characters when commenting on a blog, or signing up for an online email account, make sure your time is not wasted on a standard CAPTCHA text. Let the site owners know your time could be spent on saving humanity, or at least it's written word.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-9107601234875663527?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/9107601234875663527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=9107601234875663527&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/9107601234875663527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/9107601234875663527'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/08/will-recaptcha-save-humanity.html' title='Will reCAPTCHA Save Humanity?'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-1868019687343522433</id><published>2008-08-07T19:45:00.000+03:00</published><updated>2008-08-07T19:45:08.588+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='streaming'/><category scheme='http://www.blogger.com/atom/ns#' term='silverlight'/><category scheme='http://www.blogger.com/atom/ns#' term='nbc'/><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><category scheme='http://www.blogger.com/atom/ns#' term='olympics'/><title type='text'>The Olympics on Your PC</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/_vkjNVzG9O6o/SJrWj0xghrI/AAAAAAAAAd4/VJarSGGe_4E/s1600-h/Olympics2008.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img id="BLOGGER_PHOTO_ID_5231729828094314162" alt="" src="http://4.bp.blogspot.com/_vkjNVzG9O6o/SJrWj0xghrI/AAAAAAAAAd4/VJarSGGe_4E/s200/Olympics2008.jpg" style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;When the Olympic flame is fired up in Beijing tomorrow, and the Games begin, many people will realize that they can't watch their favorite athlete or event because of the time zone difference with China. Well this year is the first time ever that most of the events will also be available in high quality HD directly on your PC at any time you like (yes, even at work when your boss is not looking).&lt;br /&gt;&lt;a href="http://www.microsoft.com/Presspass/Features/2008/jul08/07-29qaolympics.mspx"&gt;&lt;br /&gt;NBC had partnered with Microsoft&lt;/a&gt; to deliver the 2008 Beijing Summer Games over the internet. At first NBC considered going with &lt;a href="http://www.adobe.com/devnet/video/"&gt;Adobe Flash&lt;/a&gt; which is considered the industry standard for Web video, but Microsoft was able to convince NBC that their &lt;a href="http://www.microsoft.com/SILVERLIGHT/"&gt;Silverlight&lt;/a&gt; technology will deliver better quality to the end user. They plan on streaming most of the events live, and also make them available as on-demand content for your viewing pleasure any time you like.&lt;br /&gt;&lt;br /&gt;Thanks to Silverlight, NBC can offer a unique viewing experience not even available on high end TVs. For example, they will offer a picture-in-picture view of two simultaneous events, and, for the real sports junkies,  a "control room" mode with four events streaming side by side on the screen.&lt;br /&gt;&lt;br /&gt;I can't imagine the complexity involved in delivering such a massive amount of data to so many users in real-time. This drawing (taken form an interesting &lt;a href="http://news.cnet.com/8301-13860_3-10003752-56.html"&gt;News.com&lt;/a&gt; article) tries to explain it:&lt;br /&gt;&lt;br /&gt;&lt;img id="BLOGGER_PHOTO_ID_5231731349792609842" alt="" src="http://3.bp.blogspot.com/_vkjNVzG9O6o/SJrX8Zip1jI/AAAAAAAAAeA/Lcv6e10e8Gw/s320/fromBeijingtoPC.gif" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;It is interesting to see how this "experiment" turns out, and if the masses are ready for Web TV (and if the technology stack is there too).&lt;br /&gt;&lt;br /&gt;Don't forget to check out the NBC Beijing 2008 site at &lt;a href="http://www.nbcolympics.com/"&gt;NBC Olympics&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-1868019687343522433?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/1868019687343522433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=1868019687343522433&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/1868019687343522433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/1868019687343522433'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/08/olympics-on-your-pc.html' title='The Olympics on Your PC'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vkjNVzG9O6o/SJrWj0xghrI/AAAAAAAAAd4/VJarSGGe_4E/s72-c/Olympics2008.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-527939997541321580</id><published>2008-08-05T20:18:00.003+03:00</published><updated>2008-08-06T08:55:14.495+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='stackoverflow'/><category scheme='http://www.blogger.com/atom/ns#' term='knol'/><category scheme='http://www.blogger.com/atom/ns#' term='digg'/><title type='text'>StackOverflow Private Beta</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;img style="float: right; margin-top: 10px; margin-bottom: 10px; margin-left: 10px;" src="http://bp1.blogger.com/_vkjNVzG9O6o/SJg3X518YZI/AAAAAAAAAdo/aS4XQ9fjcAU/s400/stackoverflow-logo-250.png" alt="" id="BLOGGER_PHOTO_ID_5230991850994426258" border="0" /&gt;&lt;br /&gt;I'm happy to announce that as of this morning I'm an official beta tester for the all new programmers Q&amp;amp;A site &lt;a href="http://blog.stackoverflow.com/"&gt;StackOverflow&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;StackOverflow is the brainchild of &lt;a href="http://www.codinghorror.com/blog/archives/001101.html"&gt;Jeff Atwood&lt;/a&gt; and &lt;a href="http://www.joelonsoftware.com/items/2008/04/16.html"&gt;Joel Spolsky&lt;/a&gt; who have decided to create a new knowledge transfer site specifically for programmers (&lt;a href="http://knol.google.com/k#"&gt;Knol&lt;/a&gt; for programmers if you will). The service is totally free and is designed both functionally and graphically in the spirit of Web 2.0 (and, yes, there's also a &lt;a href="http://blog.stackoverflow.com/2008/07/podcast-16/"&gt;podcast&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Each question submitted to StackOverflow is tagged by topic. Users can then answer the question, vote on it (in a very Digg like way), add their own answer and vote on the existing answer. The idea is that by using this democratic process the most relevant/accurate/knowledgeable answers will rise to the top.&lt;br /&gt;&lt;br /&gt;&lt;img style="float: right; margin-top: 10px; margin-bottom: 10px; margin-left: 10px;" src="http://lh6.ggpht.com/uri.nizan/SJheZ2ciJ-I/AAAAAAAAAdw/mr2SpunbNRM/s144/badges.jpg" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;Users are rewarded for their participation with different badges that are added to their profile. Badges include Guru, Specialist, Teacher, Student and many more. The bagdes act as incentives that are supposed to encourage users to participate.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So far, the site looks very good. The volume is suprisingly high for the first day of the private beta (with only about 500 users), and the concept seems promising. We'll see how it unfolds.&lt;br /&gt;&lt;br /&gt;Update: To apply for the beta, click &lt;a href="http://spreadsheets.google.com/viewform?key=pKxDW35algYdxrCnzW-OLag"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-527939997541321580?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/527939997541321580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=527939997541321580&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/527939997541321580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/527939997541321580'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/08/stackoverflow-private-beta.html' title='StackOverflow Private Beta'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/uri.nizan/SJheZ2ciJ-I/AAAAAAAAAdw/mr2SpunbNRM/s72-c/badges.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-5120185240460256941</id><published>2008-07-31T09:29:00.002+03:00</published><updated>2008-07-31T09:31:06.211+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='surface'/><category scheme='http://www.blogger.com/atom/ns#' term='gizmodo'/><category scheme='http://www.blogger.com/atom/ns#' term='msr'/><category scheme='http://www.blogger.com/atom/ns#' term='sphere'/><category scheme='http://www.blogger.com/atom/ns#' term='multitouch'/><title type='text'>Microsoft Sphere</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;There's been lots of rumors lately about a cool new spherical Surface computer.&lt;br /&gt;&lt;br /&gt;The original &lt;a href="http://www.microsoft.com/surface/index.html"&gt;Surface&lt;/a&gt; is a multi-touch table-top PC designed mainly for commercial use in bars and restaurants. It's really too early to tell how it will be used, and the potential is enormous, but for now this is how it is conceived:&lt;br /&gt;&lt;br /&gt;&lt;div class="youtube-video"&gt;&lt;object width="425" height="344"&gt;&lt;param value="http://www.youtube.com/v/xeWdy6eCqDc&amp;amp;hl=en&amp;amp;fs=1" name="movie"&gt; &lt;param value="true" name="allowFullScreen"&gt; &lt;embed allowfullscreen="true" type="application/x-shockwave-flash" src="http://www.youtube.com/v/xeWdy6eCqDc&amp;amp;hl=en&amp;amp;fs=1" width="425" height="344"&gt;&lt;/embed&gt;     &lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Last week Microsoft unveiled the Sphere during their annual Research Faculty Summit in Redmond. It's an amazing device that seems to be geared towards interactive exhibits in science museums, but again, only time will tell what the true killer app for the Sphere will be.&lt;br /&gt;Here's a link to a Gizmodo hands-on:&lt;br /&gt;&lt;a href="http://gizmodo.com/5030603/hands-on-with-the-microsoft-surface-sphere-prototype"&gt;http://gizmodo.com/5030603/hands-on-with-the-microsoft-surface-sphere-prototype&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And a YouTube video:&lt;br /&gt;&lt;br /&gt;&lt;div class="youtube-video"&gt;&lt;object width="425" height="344"&gt;&lt;param value="http://www.youtube.com/v/V3HGfIy_zCI&amp;amp;hl=en&amp;amp;fs=1" name="movie"&gt; &lt;param value="true" name="allowFullScreen"&gt; &lt;embed allowfullscreen="true" type="application/x-shockwave-flash" src="http://www.youtube.com/v/V3HGfIy_zCI&amp;amp;hl=en&amp;amp;fs=1" width="425" height="344"&gt;&lt;/embed&gt;    &lt;/object&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-5120185240460256941?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/5120185240460256941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=5120185240460256941&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/5120185240460256941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/5120185240460256941'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/07/microsoft-sphere.html' title='Microsoft Sphere'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-1445387508706016032</id><published>2008-07-30T10:39:00.001+03:00</published><updated>2008-07-30T10:41:35.917+03:00</updated><title type='text'>Claiming My Blog</title><content type='html'>Technorati wants me to claim my blog for them, so here goes:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://technorati.com/claim/dh5d3mc7k" rel="me"&gt;Technorati Profile&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Lets release the spiders and see what happens...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-1445387508706016032?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/1445387508706016032/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=1445387508706016032&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/1445387508706016032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/1445387508706016032'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/07/claiming-mey-blog.html' title='Claiming My Blog'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-7608524916599942280</id><published>2008-07-28T18:30:00.001+03:00</published><updated>2008-07-29T20:30:17.559+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='codeplex'/><category scheme='http://www.blogger.com/atom/ns#' term='flickr'/><category scheme='http://www.blogger.com/atom/ns#' term='flickrnet'/><title type='text'>Merging Flickr Accounts</title><content type='html'>&lt;h1&gt;How to Use the FlickrNet API to Merge Flickr Accounts&lt;/h1&gt;A few months ago, my wife finally convinced me that it doesn't make any sense to manage two separate pro Flickr accounts, and that we should transfer all the photos to one account and drop the other.&lt;br /&gt;&lt;br /&gt;So, I looked online for tools that merge Flickr accounts, and couldn't find any. Not even on &lt;a href="http://www.codeplex.com/Project/ProjectDirectory.aspx?ProjectSearchText=flickr"&gt;CodePlex&lt;/a&gt;. Don't get me wrong, I found a gazzillion mass downloaders, and uploaders and a bunch of other very cool &lt;a href="http://www.quickonlinetips.com/archives/2005/03/great-flickr-tools-collection/"&gt;stuff&lt;/a&gt;. But not a merge tool. I even contacted Flickr support, but they were totally useless.&lt;br /&gt;&lt;br /&gt;What I really wanted was a way to download the photos from the source account, and keep all the metadata I have meticulously added to the photos over the years - titles, descriptions, sets, tags, geo tags, etc. Then, upload to the target account without disrupting the flow on my &lt;a href="http://www.flickr.com/photos/urini/"&gt;photostream&lt;/a&gt;. I didn't want a bunch of old photos clogging it up.&lt;br /&gt;&lt;br /&gt;Being a developer, I decided to write my own Flickr app using the &lt;a href="http://www.codeplex.com/FlickrNet"&gt;FlickrNet&lt;/a&gt; API library, which turned out to be fun and quite enjoyable.&lt;br /&gt;&lt;br /&gt;I don't want to bore you with all the details of my implementation. I'd just like to point out some interesting aspects and potential pitfalls.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Getting an API Key&lt;/h2&gt;&lt;br /&gt;To get started, you'll need an API key from Flickr. Go to the &lt;a href="http://flickr.com/services/"&gt;Services&lt;/a&gt; page and click on the "Apply for an API Key" link.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The FlickrNet Library&lt;/h2&gt;&lt;br /&gt;Download the &lt;a href="http://www.codeplex.com/FlickrNet"&gt;FlickrNet&lt;/a&gt; library from CodePlex. You can download the binaries and reference the DLL from your code. I opted for the more geeky option, and downloaded the source code, and added the FlickrNet project to my solution. This way I could step through the code and better understand how it works.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Authorizing&lt;/h2&gt;&lt;br /&gt;If you want your app to do more than merely download public photos, your users will need to authorize it. The process is quite simple, but not that obvious. You need to launch a web browser and navigate to a Url. Here's what you do to get the Url.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;FlickrNet.Flickr flickr = new FlickrNet.Flickr(ApiKey, SharedSecret);&lt;br /&gt;string frob = flickr.AuthGetFrob();&lt;br /&gt;string url = flickr.AuthCalcUrl(frob, FlickrNet.AuthLevel.Write);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;AuthLevel can be one of the following values:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Read&lt;/li&gt;&lt;li&gt;Write&lt;/li&gt;&lt;li&gt;Delete&lt;/li&gt;&lt;li&gt;None&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Once you have the Url, launch the default web browser.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;System.Diagnostics.Process.Start(url);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At this point, the user needs to login to her account and authorize the application. Now you can retrieve the authorization token from Flickr.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;FlickrNet.Flickr flickr = new FlickrNet.Flickr(ApiKey, SharedSecret);&lt;br /&gt;FlickrNet.Auth auth = flickr.AuthGetToken(frob);&lt;br /&gt;string token = auth.Token;&lt;br /&gt;string userName = auth.User.Username;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Downloading Photos Metadata&lt;/h2&gt;&lt;br /&gt;This is a relatively simple process. Use the PhotoSearchOptions object to define the photos you are interested in (this could be based on a user, tag, etc.). The PhotosSearch method doesn't return all the photos it finds in one call, rather it returns "pages" of photos. You use the same PhotoSearchOptions to move to the next page and bring the next batch of results.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Get the user Id based on the user name&lt;br /&gt;FlickrNet.Flickr flickr = new FlickrNet.Flickr(ApiKey, SharedSecret);&lt;br /&gt;FlickrNet.FoundUser user = flickr.PeopleFindByUsername(UserName);&lt;br /&gt;string userId = user.UserId;&lt;br /&gt;&lt;br /&gt;// Create a PhotoSearchOptions object&lt;br /&gt;FlickrNet.PhotoSearchOptions options = new FlickrNet.PhotoSearchOptions();&lt;br /&gt;options.UserId = userId;&lt;br /&gt;&lt;br /&gt;// Retrieve the user's photos&lt;br /&gt;FlickrNet.Photos photos = flickr.PhotosSearch(options);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Downloading Sets Metadata&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Get all the photosets for the user&lt;br /&gt;FlickrNet.Photosets sets = flickr.PhotosetsGetList(userId);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Linking Photos to Photosets&lt;/h2&gt;&lt;br /&gt;This is where it gets a little  tricky. See, Flickr allows photos to belong to 0-n sets. So, in theory I had to account for photos that are not part of any set, photos that belong to a single set, and for photos that belong to multiple sets. Since almost all of my photos belong to just one set, I've decided to cut myself some slack, and only deal with 0-1 sets per photo.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;foreach (FlickrNet.Photoset set in sets.PhotosetCollection)&lt;br /&gt;{&lt;br /&gt; ...&lt;br /&gt; ... //Save photoset metadata&lt;br /&gt; ...&lt;br /&gt;&lt;br /&gt; // Get the photoset's photos&lt;br /&gt; FlickrNet.Photoset set1 = flickr.PhotosetsGetPhotos(set.PhotosetId);&lt;br /&gt; FlickrNet.Photo[] setPhotos = set1.PhotoCollection;&lt;br /&gt; foreach (FlickrNet.Photo photo in setPhotos)&lt;br /&gt; {&lt;br /&gt;  ...&lt;br /&gt;  ... // Link photo to photoset&lt;br /&gt;  ...&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Downloading Photos&lt;/h2&gt;&lt;br /&gt;The one thing to keep in mind here is that Flickr stores multiple versions of each photo (with different resolutions), so you want to make sure you download the largest one.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;FlickrNet.Sizes sizes = flickr.PhotosGetSizes(photo.PhotoId);&lt;br /&gt;FlickrNet.Size size = sizes.SizeCollection[sizes.SizeCollection.Length - 1];&lt;br /&gt;string sourceUrl = size.Source;&lt;br /&gt;string fileName = size.Source.Substring(size.Source.LastIndexOf("/") + 1);&lt;br /&gt;&lt;br /&gt;WebClient client = new WebClient();&lt;br /&gt;client.DownloadFile(sourceUrl, fileName);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Uploading Photos&lt;/h2&gt;&lt;br /&gt;Uploading the photos is easy. I just had to change the dates on the photo after the upload to make sure it shows up in the correct place in my photostream based on the upload date, and add the GEO tags, if they existed in the original metadata.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;FlickrNet.Flickr flickr = new FlickrNet.Flickr(ApiKey, SharedSecret, AuthToken);&lt;br /&gt;string photoId = flickr.UploadPicture(fileName, title, description, tags, true, false, false);&lt;br /&gt;flickr.PhotosSetDates(photoId, datePosted, dateTaken, FlickrNet.DateGranularity.FullDate);&lt;br /&gt;flickr.PhotosGeoSetLocation(photoId, latitude, longitude);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Creating Photosets&lt;/h2&gt;&lt;br /&gt;Creating a Photoset is a breeze. Just remember to assign a primary photo to the set, and make sure the photo id is the new photo id (from the target account) not the old one.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;FlickrNet.Flickr flickr = new FlickrNet.Flickr(ApiKey, SharedSecret, AuthToken);&lt;br /&gt;FlickrNet.Photoset set = flickr.PhotosetsCreate(title, description, primaryPhotoId);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you can add the photos (already uploaded) to the newly created set.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;flickr.PhotosetsAddPhoto(photosetId, photoId);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;br /&gt;Working with the FlickrNet API is both easy and fun. I'm personally happy I've decided to write my own app, I think I've learned a lot from it. If you are like me, and believe that &lt;a href="http://www.hanselman.com/blog/CategoryView.aspx?category=Source+Code"&gt;reading code makes you a better developer&lt;/a&gt;, I recommend downloading the source code version and using is in your solution.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Related Links&lt;/h2&gt;&lt;br /&gt;&lt;a href="http://blogs.msdn.com/coding4fun/archive/2006/11/22/1126978.aspx"&gt;Coding4Fun&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.codeplex.com/flickrexplorer"&gt;Flickr Explorer&lt;/a&gt;&lt;br /&gt;&lt;a href="http://flickr.com/services/api/"&gt;Flickr API&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-7608524916599942280?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/7608524916599942280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=7608524916599942280&amp;isPopup=true' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/7608524916599942280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/7608524916599942280'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/07/merging-flickr-accounts.html' title='Merging Flickr Accounts'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2232785552580074568.post-8420591353332763073</id><published>2008-07-14T13:00:00.029+03:00</published><updated>2008-07-15T21:35:14.418+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='newton'/><title type='text'>A New Beginning</title><content type='html'>New beginnings are hard. Starting something new - a new job, start-up, or even a blog - is not a simple task. It takes effort. It's against our nature, really. &lt;a href="http://en.wikipedia.org/wiki/Isaac_Newton"&gt;Newton&lt;/a&gt;, with his first law of motion explains it well (I'm paraphrasing):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right;" src="http://bp3.blogger.com/_vkjNVzG9O6o/SHzlJjE7KCI/AAAAAAAAAb4/P4xfKKp6t_c/s200/newtons_cradle.jpg" alt="" id="BLOGGER_PHOTO_ID_5223301620040411170" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;"An object at rest tends to stay at rest and an object in motion tends to stay in motion with the same speed and in the same direction..."&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We are conditioned to do what we know, what we're good at. Stay the course. Most of us are afraid of change, of the unknown. It's natural.&lt;br /&gt;&lt;br /&gt;But Newton realized that things are not that simple, so he added:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;"unless acted upon by an external, unbalanced force."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Several weeks ago I started a new job as a developer at SAP Labs in Israel (a definite "unbalanced force"). This new job involves quite a bit of research around the latest Microsoft technologies (WCF, WPF, MOSS, OBA, VSTO, etc.). So I figured, I might as well share my experiences with other developers in the community, and what better way than an online blog.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right;" src="http://bp3.blogger.com/_vkjNVzG9O6o/SHzor-2eFbI/AAAAAAAAAcA/x4y7tcjEGaI/s200/sap_hall.jpg" alt="" id="BLOGGER_PHOTO_ID_5223305510146414002" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;With everything I have to learn these days (a new company, a new product, new technologies) I know I'll be drinking from the fire hose for a while, but I honestly believe that writing will actually help me make sense and keep track of it all.&lt;br /&gt;&lt;br /&gt;So, hang on, and enjoy the ride.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2232785552580074568-8420591353332763073?l=www.sapiencoder.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sapiencoder.com/feeds/8420591353332763073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2232785552580074568&amp;postID=8420591353332763073&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/8420591353332763073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2232785552580074568/posts/default/8420591353332763073'/><link rel='alternate' type='text/html' href='http://www.sapiencoder.com/2008/07/new-beginning.html' title='A New Beginning'/><author><name>Uri Nizan</name><uri>http://www.blogger.com/profile/08516609574024887993</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13543619149282558843'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_vkjNVzG9O6o/SHzlJjE7KCI/AAAAAAAAAb4/P4xfKKp6t_c/s72-c/newtons_cradle.jpg' height='72' width='72'/><thr:total>1</thr:total></entry></feed>