Bookmark and Share

Inane Generic Usage

by KodefuGuru 29. September 2009 19:04

in·ane (ĭn-ān') adj. in·an·er, in·an·est
One that lacks sense or substance.

The American Heritage® Dictionary of the English Language, Fourth Edition
Copyright © 2009 by Houghton Mifflin Company.
Published by Houghton Mifflin Company. All rights reserved.

One of the first signs that code needs to be refactored is that it looks complex. Complex solutions are rather easy to come by, but are far less maintainable than their harder to write, easier to read brethren. Like any well-crafted sculpture, it didn’t like start that way, and it requires work to craft that beautiful piece of code. But sometimes you come across code for which there is no reason for the complexity. It appears that someone just wanted to type extra characters throughout their code.

A while back, I analyzed another developer’s code that used a lot of generics. Since nothing was being returned from many of these methods, I decided to look at the signature to discover why the generics were necessary. Here’s one of them.

public static void PrintReport<T>(T report)
{
    WriteEvent(report.ToString());
    IReport r = report as ClientReport;
    if (r != null)
    {
        //Do stuff
    }
    r = report as EmployeeReport;
    if (r != null)
    {
        //Do stuff
    }
}

Looking at this, you may notice there is absolutely no reason for the generic declaration. There are no constraints on it, so it is the same thing as declaring the parameter as an object. Everwhere this was used, it was calling PrintReport<ClientReport>(report) and so on. I pointed this out to the developer, and said it should be refactored because a) the generic adds no value, and b) there are object oriented solutions that are much better than static methods and casting. Of course, this is what I got back.

public static void PrintReport<T>(T report) where T : IReport
{
    WriteEvent(report.ToString());
    IReport r = report as ClientReport;
    if (r != null)
    {
        //Do stuff
    }
    r = report as EmployeeReport;
    if (r != null)
    {
        //Do stuff
    }
}

There’s now a constraint! A constraint that has no meaning other than preventing someone from printing an object that isn’t an IReport. Of course, the same thing could be accomplished by making the parameter be an IReport rather than a T. I then showed the developer how the generic adds absolutely no value (I think I saw a grimace as I cut all of those <xReport>s out of the myriad of classes making that call). I then explained how when I see things like casting in a method like that, it makes me consider that perhaps that behavior belongs on the class, perhaps called by a method defined on the interface.

public static void PrintReport(IReport report)
{
    WriteEvent(report.ToString());
    report.DoStuff();
}

For future reference, inane usage of generics involves using a generic on a method, not involving a parameterized class, where 1) the return type isn’t the generic type, and 2) the constraint isn’t new() or class.

If someone can think of a situation where this is actually useful, post a comment.

Also, another quick lesson. The developer did place PrintReport<ClientReport> in many places, but did you know that this wasn’t even necessary? With generic methods, you get generic inference. The compiler will determine the type for you. This  is very handy with LINQ. Without generic inference, you would have to write ugly methods like strings.Where<string>(s => s.StartsWith(“s”)).

Bookmark and Share

Don’t Instantiate Then Assign

by KodefuGuru 15. June 2009 10:09

Here’s an example of some bizarre code I’ve seen recently. I’ve changed the variable names, but this is what the developer was doing.

Customer customer = new Customer();
customer = customers[response.CustomerKey];

The first line is completely unnecessary. If you instantiate an object, then assign the object to another instance, the first instance sticks around and eventually gets collected. However, there’s no reference to it. You can never use that instance. Here’s how it should look.

Customer customer = customers[response.CustomerKey];

I would assume that it was dirty code that should have been cleaned before checking into version control, except for the fact the lines were adjacent. For the life of me, I don’t know why anyone would create an unnecessary instance. Any ideas?

Bookmark and Share

ref on Reference Type?!

by KodefuGuru 12. June 2009 17:02

I recently came across a piece of code that had almost every parameter for every method marked with the ref keyword. This keyword is primarily used to pass a value type by reference, but this one even had reference types marked with the keyword. This made me wonder, why exactly would you ever want to pass a reference type as a ref parameter? Page Brooks and I discussed this at a user group meeting earlier in the week, but neither of us had an answer.

So, I turned to that ever-trusty resource, the MSDN Library. The trick is that passing by ref allows you to change the reference. Take this code for example:

class Program
{
    static void Main()
    {
        var message = new Message("Created by Main");
        Console.WriteLine(message.Text);
        Method(message);
        Console.WriteLine(message.Text);
    }

    static void Method(Message message)
    {
        message = new Message("Created by Method");
    }

    class Message
    {
        public string Text { get; set; }
        public Message(string text)
        {
            Text = text;
        }
    }
}

This will have the following output:

Created by Main
Created by Main

The reason “Created by Method” isn’t printed is because the parameter message was given a new reference. The message variable in Main() has its original reference. Now let’s see what happens when we change Method() to use a ref parameter.

class Program
{
    static void Main()
    {
        var message = new Message("Created by Main");
        Console.WriteLine(message.Text);
        Method(ref message);
        Console.WriteLine(message.Text);
    }

    static void Method(ref Message message)
    {
        message = new Message("Created by Method");
    }

    class Message
    {
        public string Text { get; set; }
        public Message(string text)
        {
            Text = text;
        }
    }
}

Now our output looks like this":

Created by Main
Created by Method

The ref keyword has modified Method() so that now it will allow the reassignment of message.

Wait, isn’t that just like the out keyword?

Actually, it’s almost identical to the out keyword. I can even replace ref with out in the above example and it will work exactly the same. However, there is one difference: you need to pass an assigned variable with ref, but you can pass an unassigned variable with out. It’s semantically odd to expect an out parameter to be used as input, although nothing prevents you from doing this. It would be best to stick with ref if you wanted in/out.

Now that I’ve explained the behavior one gets using the ref keyword with a reference type, allow me to make a bold claim. The ref and out keywords are code smells.

There’s almost no reason to ever use the ref or out keywords with a reference type. In fact, in the code where I noticed the ref keywords littered everywhere, it wasn’t reassigning parameters in the methods. The ref declarations were merely a waste of space that caused the Spock eyebrow to raise. If you are reassigning the parameter, I recommend that you take another look to see if it can be done another way. For example, the ref version of the method could easily be refactored to return a message object that is assigned to the message variable:

static void Main()
{
    var message = new Message("Created by Main");
    Console.WriteLine(message.Text);
    message = Method(message);
    Console.WriteLine(message.Text);
}

static Message Method(Message message)
{
    return new Message("Created by Method");
}

I feel that this code is much clearer than using ref. In the method that’s making the call, Main(), it’s clear that message is being reassigned. In the ref version, you have to look through Method() to see whether or not it is being reassigned.

The keywords are there because there are situations where they are useful; DateTime.TryParse() comes to mind. However, like most cool tech in C#, it can be abused.

KodefuGuru.GetInfo()

Chris Eargle
LinkedIn Twitter Technorati Facebook

Chris Eargle
C# MVP, INETA Community Champion


MVP - Visual C#

 

INETA Community Champions
Friend of RedGate
Telerik .NET Ninja
Community blogs & blog posts

I am a #52er

I have joined Anti-IF Campaign


World Map

Tag cloud

Disclaimer

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

© Copyright 2010