Matt Casto's .NET Journal RSS 2.0
 Thursday, May 24, 2007
I was writing some code today for a utility method to format names. You know, one of those functions that you find yourself writing even thought you've probably done it lots of times in the past, but you can't find the previous versions.

I got to the part where it returns different values if other values are available, and realized something as I wrote the following code.

if (!string.IsNullOrEmpty(lastName))
return lastName;
else if (!string.IsNullOrEmpty(firstName))
return firstName;
else if (!string.IsNullOrEmpty(middleName))
return middleName;
else
return string.Empty;

I realized that I could probably condense that into one line if I could chain together the variables with null coalescing operators. "What the hell," I thought, "I'll give it try and see if it compiles."

return lastName ?? firstName ?? middleName ?? string.Empty;

Sure enough, it compiles. And it works!

It's always a nice feeling to write beautiful code.

Labels:

Thursday, May 24, 2007 11:57:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [6] -
c#
 Saturday, March 31, 2007
The support for extending existing types with custom methods in Orcas is something I've been fairly excited about for a while. Extension methods provide way to inject your static own method into someone else's type, where it will appear as if it was an instance method defined on that type.

A Simple Example

In many of my projects, I have enumerations decorated with a description attribute and a static method in a utility class which will return the description of an enum value. This provides a way to have a user friendly description of an enum value. Now I can take that method in the utility class, modify it slightly to be an extension method, and have better looking, more intuitive and highly discoverable code.

Let's say we have an enumeration of report titles.

public enum ReportTitle
{
[EnumDescription("TPS Report")]
TPS,
[EnumDescription("Summary Report")]
Summary,
[EnumDescription("Expense Breakdown Report")]
[EnumDescription("Expense Report")]
ExpenseBreakdown,
Unknown
}

My .NET 2.0 utility method to get enum descriptions looks like this:

public static string GetEnumDescription(Enum enumValue)
{
if (enumValue == null)
return string.Empty;

Type enumType = enumValue.GetType();
Type descType = typeof(EnumDescriptionAttribute);
FieldInfo fi = enumType.GetField(enumValue.ToString());
object[] descAttrs = fi.GetCustomAttributes(descType, false);

// concatenate multiple descriptions
string desc = string.Empty;
foreach (EnumDescriptionAttribute descAttr in descAttrs)
desc += (desc.Length == 0)
? descAttr.Description
: ", " + descAttr.Description;

if (desc == string.Empty)
desc = enumValue.ToString();

return desc;
}

I don't think I really need to show the EnumDescriptionAttribute code or code that gets report titles, because there are no surprised there. As an aside, if anyone wonders why I'm not using System.ComponentModel.DescriptionAttribute, its because that attribute doesn't support multiple decorators on a single type.

This example code re-written with Visual Studio Orcas March CTP looks like this:

public static string Description(this Enum enumValue)
{
if (enumValue == null)
return string.Empty;

var enumType = enumValue.GetType();
var descType = typeof(EnumDescriptionAttribute);
var fi = enumType.GetField(enumValue.ToString());
var descAttrs = fi.GetCustomAttributes(descType, false);

// concatenate multiple descriptions
var desc = string.Empty;
foreach (EnumDescriptionAttribute descAttr in descAttrs)
desc += (desc.Length == 0)
? descAttr.Description
: ", " + descAttr.Description;

if (desc == string.Empty)
desc = enumValue.ToString();

return desc;
}

And the enum value's description is easily found through intellisense:

report.Description()

I can also take it a step further by refactoring my code to include a generic extension method that returns all custom attributes from a value's type, and another that joins an array of strings into one comma delimited string.

public static string Description(this Enum e)
{
var attrs = e.CustomAttributes<Enum, EnumDescriptionAttribute>();
var descs = new List<string>(attrs.Count);
attrs.ForEach(a => descs.Add(a.Description));
return descs.Join();
}

public static List<TAttr> CustomAttributes<T, TAttr>(this T obj)
where TAttr : System.Attribute
{
var fi = obj.GetType().GetField(obj.ToString());
var attrs = fi.GetCustomAttributes(typeof(TAttr), false);
return new List<TAttr>((TAttr[])attrs);
}

public static string Join(this List<string> values)
{
return values.Join(", ");
}
public static string Join(this List<string> values, string delimiter)
{
string result = string.Empty;
values.ForEach(v => result += (result.Length == 0) ? v : delimiter + v);
return result;
}

Of course, the custom attributes method still needs to be refactored to support other types.

Useful For More Than Utility Methods

Since extension methods can be used on sealed classes, they could also be used to hide methods from a publicly distributed assembly by putting extension methods in a non-distributed assembly. Of course, you could also accomplish this with a partial class. Sure, there are probably plenty of reasons why this would be a bad idea, but I get hyper over syntax additions, even if they're not really offering anything that you couldn't do before with more mundane code.

There seems to be a lot of controversy over whether extension methods are a good practice and should be part of object oriented programming. Some of the reasons I've seen people use to not use extension methods are that they complicate code discoverability, they will make code reviews more difficult, they don't follow object oriented principles, or even that they contradict best practices published previously by Microsoft. I think Scott Wisniewski of the VB Compiler Team does a great job addressing the benefits of extension methods in his introduction to an excellent series of in depth blog posts, which are also a good read to get a VB.NET perspective on the new feature.

Labels: ,

Saturday, March 31, 2007 2:08:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
betas | c#
 Friday, March 30, 2007
Every good developer knows that you don't want to spend too much time optimizing code as you're writing it. Premature optimization is Eric Gunnerson's 4th deadly sin of programming. I don't think that's over-exaggerating the problem.

But how do you know when you're going too far? Let's say you're writing a class that will be transferring data to or from somewhere else. When you find yourself thinking something like, "What if there's a ton of data being transferred? I need to change the format and consolidate information to make sure we send as little as possible." you're optimizing prematurely. You don't know that you'll run into that situation, and if you fix it now you're spending time fixing a problem that may never exist.

I recently ran into a situation like this, but instead I was thinking something more like, "According to the customer, there will possibly be tons of data to work with." Now I know that there will be a lot of data, so it's not premature to think about how that will affect performance. However, I don't know how bad my code will perform with the amount of data we'll have at production, so I don't know for sure that I need to spend time on the issue. There actually isn't an issue yet, even though the likelihood of a problem is much higher. Do I want to optimize in this case?

The feedback I got from my team was to not spend any time on optimization until the problem actually exists. I think there's a fine line between premature optimization and optimization based on known factors. I'm still not sure if I'm doing the right thing, but I'm going to follow the methodology and see how it works out.

Labels:

Friday, March 30, 2007 6:43:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
methodologies
 Monday, March 12, 2007
I made my first attempt at TDD last week and I definitely like the results. I feel like writing a test first lead me to a much more structured way to writing my code. I felt more in control of where I was going since I already knew what my code would ultimately need to do. Also, having a list of build errors (because of methods being called that don't exist) kept me focused on what I needed to be doing next. I think I still have a lot to learn about this, but I'm happy about my experience so far.

One thing I need to work on is code coverage, but I still have that nagging feeling that spending too much time writing test cases is slowing me down.

Labels:

Monday, March 12, 2007 8:14:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [2] -
methodologies
 Tuesday, March 06, 2007
I was about to post about how upset I was that the March CTP of Orcas was only available as an over 4GB! Virtual PC image. Then, when searching for a link for this blog post, I found "Orcas" Related CTP Downloads, which has a link for a self-extracting installation to use instead of an image. Huzzah!

Labels:

Tuesday, March 06, 2007 11:22:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
betas
 Thursday, February 01, 2007
I just received the copy of Windows Developer Power Tools O'Reilly sent me as thanks for my participation on Windows Developer Tools Day two weeks ago. It's a massive book, but seems to be organized so well that it will be easy to read a chapter or two each evening.

In fact, after seeing Jim Holmes' post about the WinDevPowerTools Rollout, I went there and set up my own profile. As tools are added to the site, and as I'm able to check out more tools that I read about on the book, I'll update my profile there.



A lot of the tools that I use regularly either haven't been added to the list or aren't covered by the book. I'm not sure about what's covered by the book yet because I haven't had any time to dig into it yet. I would love it if I could add tools that I use that might not have been covered by the book.

Labels:

Thursday, February 01, 2007 10:07:00 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
tools
 Friday, January 26, 2007
While searching for the VS 2005 CMD Prompt Here shell extension I stumbled upon some other tools that I really like. First of all, the Clean Sources application does exactly the same thing as a command line application that I whipped up two days ago, except it provides better feedback and is integrated into the explorer shell.

But the best find was by Omar Shahine, the same author of Clean Sources - Clipboard To Image. This application takes care of the manual steps I constantly find myself doing when I take screenshots of application or diagrams, and it even defaults to the PNG format! (yay)

Now I just have to integrate it into SlickRun and move on to being more productive.

** Note:
After playing with Clipboard to Image for a bit, I noticed that if you try to overwrite an existing image, the overwrite never actually happens. So you just have to create a new image each time.

Labels:

Friday, January 26, 2007 11:50:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [2] -
tools
You would think that calculating the age of a person based on their date of birth would be simple, but it's more tricky than that. Like most problems in programming, things are never what they seem at first glance.

The .NET framework has some greate objects for working with dates and time - System.DateTime and System.TimeSpan, and it's trivial to do a calculation like the following.
public int CalculateAge(DateTime birthDate)
{
return DateTime.Now.Year - birthDate.Year;
}

Our return value has the correct calculation of the difference in years between the current date and the birth date, but that isn't what a typical person considers an age. The following test cases will show that this function isn't correct.
[TestMethod]
public void TestAgeCalculation()
{
DateTime dob = DateTime.Now.AddYears(-1);

// born a year ago yesterday
dob = DateTime.Now.AddDays(-1);
Assert.AreEqual(CalculateAge(dob), 1);

// born a year ago today
dob = DateTime.Now.AddDays(1);
Assert.AreEqual(CalculateAge(dob), 1);

// born a year ago tomorrow
dob = DateTime.Now.AddDays(1);
Assert.AreEqual(CalculateAge(dob), 0);
}

The third test case fails because age doesn't automatically equal the difference in years. Another solution that comes quickly to mind is to get a TimeSpan which is the difference between now and the date of birth, but there's no method that converts a TimeSpan to the number of years. What you can get from a TimeSpan is the number of days, which you can then divide to get years.
public int CalculateAge(DateTime birthDate)
{
TimeSpan ageTime =
DateTime.Now.Subtract(birthDate);
return Convert.ToInt32(Math.Floor(
ageTime.Days / 365.25));
}

This fails too because we consider ourselves to be a year older on our birth day, even though we're actually not a year older until the day after our birthday. Also, I don't really like the division by 365.25 because it's not entirely accurate. Of course, that's all ignoring the time of birth in the equation.

Here is what I finally came up with to accurately calculate an age from date of birth.
public int CalculateAge(DateTime dob)
{
int years = DateTime.Now.Year - dob.Year;
if (dob.AddYears(years) > DateTime.Now)
years--;
return years;
}

Definitely not all that complicated, but more tricky than I imagined it would be when I started to tackle the solution. This is one of those things that all programmers run into at least a handful of times during their career and, I'd hope, take some time to make sure their code is accurate, but it's easy to just throw code out there that works 98% of the time.

Labels:

Friday, January 26, 2007 7:47:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [2] -
c#
 Friday, January 19, 2007
I was walking down a empty hall, not sure where I was. Ah, there's the room I was looking for. As I walked through the door, the people inside turned around and looked at me. Some had clothes with penguins on them, or carried devices with logos of partially eaten fruit. "Where you from, cuz?" the one up front said. Oh no, I'm dead now!

Okay, so maybe that's what I imagined it might be like walking into a session focused on Python. I was definitely outside of my Microsoft comfort zone. But that's what CodeMash is supposed to be about, so I figured I'd go out of my way to see a presentation on something almost totally unknown. It turned out to be nothing like my overactive imagination, and I don't think I stood out at all. I'm not sure why I expected anything different, really.

I was at Mark Ramm's presentation on SQL Alchemy. Honestly, I wasn't following all of the details and I'm not clear on what I took away from it. At the very least I gained a basic understand, exposure or awareness of something that I wouldn't normally see. It was definitely interesting, but the Q & A was totally over my head so I ducked out early.

There are a few things I've noticed about CodeMash so far. As the conference has progressed, I've noticed people being a lot more comfortable asking questions or giving comments without being prompted. So far, the sessions I've been in where the audience contributed have been the best experiences.

Also, I never imagined I'd be live blogging, but today I found that I had a lot to say. I still don't like the term and wouldn't really admit to doing it.

Oh yeah, and I sat near a guy who totally looked like Don Knotts.

Labels:

Friday, January 19, 2007 12:21:00 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
codemash
Today's sessions that I plan on attending definitely focus on data access, which actually has me excited after the ridiculous models at my last job.

I just got out of Scott Guthrie's keynote on LINQ. LINQ definitely has me excited, and I need to play with the release from last year.

Right now I'm in Dave Donaldson's session on NHibernate. Code with no more stored procedures? Excellent! I'll follow up later with my impressions.

Impressions:
Dave has written an interesting framework called NHibernateRepository that I might look into using. Its not integrated into the NHibernate project, but Dave said that he's going to eventually look into getting it incorporated into a contrib project.

I was suspicious of what the performance would be with an O/R mapper, but since NHibernate uses parametrized queries, its virtually no different from stored procedures in that respect. From Dave's demo I got the impression that if you need to do tricky things, the way to go about it is using a syntax called HQL, which looks just like hard coded queries, and weren't we supposed to get away from that?

Differences between NHibernate and LINQ were also on my mind. LINQ has a nice visualizer that shows the actual query when you're debugging, which is a lot nicer than having to use the profiler or read a log file. It occurred to me when NHibernate's insert/update/delete features were being demonstrated that I haven't seen any examples of updating data with LINQ. Is LINQ supposed to be used solely to query data, and not modify it?

You have to write a lot of configuration and code by hand with NHibernate .... unless you use a code generator. Dave demonstrated a CodeSmith template that would (have if it hadn't crashed) create entity classes and config from your database. But the code still wasn't pleasing to look at. I wondered if, like Dave's NHibernateRepository library, if anyone has adapted the Data Access Application Block to work with NHibernate, or another O/R Mapper for that matter.

Another, maybe totally unrelated, thought was "where is the c# in stored procedures?" C# embedded in stored procedures was a feature that was highly touted in .NET 2.0, but I have yet to see anyone use it other than code in MSDN documentation. Is this feature really something people want to use? Don't we want to stay away from business logic in stored procedures? Yeah.

I'm in Mark Ramm's session on O/R Mapping with SQL Alchemy right now, which is definitely outside of my comfort zone. This is what CodeMash is supposed to be all about, so we'll see how I like it.

Labels:

Friday, January 19, 2007 10:00:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
codemash
 Thursday, January 18, 2007
I made to CodeMash with about 10 minutes to register before the morning keynote. I'm really glad I made it to the keynote, which was about domain specific languages. Neal Ford did a really good job and I can't wait to go back through his presentation and compare it to my notes about ways I could incorporate DSL into some various ideas I have. I'm not ready to share those ideas yet, but when I feel like they're not totally silly I'll be posting them here.

Other sessions I attended today were ...
  • Smart Clients - more like an introduction and didn't see anything I didn't already know.
  • Ruby on Rails - This was a pretty cool session on Active Record and talking about how Rails works. My only complaint is that I should have sat closer because I couldn't read the text on the code examples.
  • TDD - a session about benefits from TDD beyond the main idea of units tests - very good.
  • An excellent keynote by Bruce Eckel that ... well ... I'm not sure what the focus was, but it was very entertaining and sparked some interesting thoughts.
I'm not sure what to expect tomorrow, but if its anything like today I'm going to be rocked. I know it starts off with Scott Guthrie doing a talk on LINQ, which I'm stoked for.

Labels:

Thursday, January 18, 2007 11:15:00 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
codemash
Central Ohio Day of .NET

About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2008
Matt Casto
Sign In
All Content © 2008, Matt Casto
Theme based on DasBlog theme 'Business' created by Christoph De Baene (delarou)