Matt Casto's .NET Journal RSS 2.0
 Sunday, July 15, 2007
Bill Wagner's article Create Mixins with Interfaces and Extensions Methods on MSDN captures the excitement that I have for extension methods. Basically, using a minimal interface and several extension methods that extend that interface, C# is closer to multiple inheritance than ever before.

Rather than duplicating Bill's examples, you should just go ahead and read his article.

Labels:

Sunday, July 15, 2007 1:40:00 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
c#
 Tuesday, July 10, 2007
As part of a significant refactoring effort, I was moving code from one project to another and accidentally made a change that resulted in a much different result than I expected. I'm getting a date from a database query, but that date column allows nulls. Here's what I had before:
DateTime? someDate = (dataReader["DATE_COLUMN"]) != null
? Convert.ToDateTime(dataReader["DATE_COLUMN"])
: new DateTime?();

And here's what I ended up typing in my refactored code:
DateTime? someDate = (dataReader["DATE_COLUMN"]) != null
? Convert.ToDateTime(dataReader["DATE_COLUMN"])
: default(DateTime);

I didn't even realize I'd written the code differently, probably because they should have the same result, but they didn't. The first version, new DateTime?(), has the value null, while the second version, default(DateTime), has the value 1/1/0001. That's not even a valid date in SQL Server!

The following code in a console application will demonstrate different default values.
Console.WriteLine("new DateTime?() = {0}", new DateTime?());
Console.WriteLine("default(DateTime) = {0}", default(DateTime));
Console.WriteLine();
Console.WriteLine("new TimeSpan?() = {0}", new TimeSpan?());
Console.WriteLine("default(TimeSpan) = {0}", default(TimeSpan));

Console Output

It looks like a new instance of a nullable object will always be null, while the default value for that type won't be null. Of course, I came to this conclusion with very limited tests, but it's good enough for me. ;-)

Labels:

Tuesday, July 10, 2007 6:20:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [4] -
c#
 Friday, May 25, 2007
While coding this morning, I needed to quickly find an entity object in an array, so I used the generic List's Find method.
Entity match = new List<Entity>(someEntityArray).Find(
delegate(Entity test) { return test.Id = someId; } );

Later, when I tried to build, I was getting a very unhelpful compiler errors.
error CS1502: The best overloaded method match for 'System.Collections.Generic.List.Find(System.Predicate)' has some invalid arguments
and
error CS1503: Argument '1': cannot convert from 'anonymous method' to 'System.Predicate'


These errors didn't make sense to me, but I tried creating a Predicate object, then passing that into the Find method by moving some code around.
Predicate<Entity> pred = delegate(Entity test) { return test.Id = someId; };
Entity match = new List<Entity>(someEntityArray).Find(pred);

At this point, I'm getting the compile errors
error CS1662: Cannot convert anonymous method block to delegate type 'System.Predicate' because some of the return types in the block are not implicitly convertible to the delegate return type
and
error CS0029: Cannot implicitly convert type 'long' to 'bool'


"Ah, now we're getting somewhere," I thought to myself, "but why would the anonymous method be converting long to bool? D'oh!"

I bet a good portion of other developers reading this blog (assuming anyone does) probably noticed the problem at once. The old single equals sign instead of double equals sign in a comparison. Going back to my original code and adding an equals sign compiles fine.
Entity match = new List<Entity>(someEntityArray).Find(
delegate(Entity test) { return test.Id == someId; } );

This all got me wondering why the compile error was so far off. I did a quick google for the original error message and came across this blog post where Avner Kashtan had a similar problem and he ended up getting feedback from Microsoft that this is the correct behavior, although misleading, and things should be improved with lambda expressions.

Labels:

Friday, May 25, 2007 10:19:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
c#
 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, January 26, 2007
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 05, 2007
Some of the things I read in R. Aaron Zupancic's blog post New Operator in C# 2.0: ?? caught me off guard. I swear I'd read through several articles listing changes and additions in .NET 2.0 and C# 2.0, including MSDN, but never ran across the ?? operator. I even checked the C# Operators documentation and didn't see ?? Operator until the second pass, because it's grouped in the assignment category. I expected it to be grouped in the conditional category with ?:, but now I'm wondering why ?: isn't considered an assignment operator?

While the null coalescing operator is cool and looks nice in code, I'm not sure how much I'll use it. Most of my code where I'm checking whether a variable is null throws an exception or takes some other action instead of using another value in place of the null variable.

The most interesting thing that I noticed was the following comment from Owen Blacker:
It's even cooler than that. The null coalescing operator does type forcing, too.

Imaging you have this:

public int? GetUserID() { ... }

int userID = GetUserID() ?? -1;

The ?? operator not only provides an alternative value if GetUserID returns null, but it also does type conversion, from the int? result of GetUserID to a non-nullable Int32, if it returns a non-null result.
I was really thrown off by the int? part of the example code in the comment above. I eventually found that it's the same thing as NullableType. That's really nice syntax to replace using a nullable value type. Before I knew what it was, I immediately assumed that it was something like a nullable type. The question mark following the value type suggests that you're not sure that the returned value will be integer or not. I will definitely get some mileage out of this information. Finally, a way to represent a value from a SQL bit column in code.

I'm not sure why I forgot about Nullable Types. I do recall reading about them in the feature list for a .NET 2.0 beta, but I don't remember seeing anything about them in any "what's new" articles. Maybe I just missed it.

Back to the operator, I think the next example reads a lot cleaner than the following one which uses the NullableType's GetValueOrDefault() method. Of course, its probably better to use that method so you aren't hard coding a value type's default value in code (not that it would change).

Using the ?? operator:
int? x = null;
int y = x ?? 0;
Using the GetValueOrDefault() method:
int? x = null;
int y = x.GetValueOrDefault();
It's interesting that the null coaelscing operator post is making rounds now, almost exactly a year after it's original posting.

Labels:

Friday, January 05, 2007 7:43:00 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
c#
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)