Look at the following piece of code:

private void DbMove(CopyInfo info)
{
    string targetDb = GetTargetDB(info);
    if (targetDb.Equals(string.Empty))
        return;

    PrepDbsForCopy(info.SourceDb, targetDb);

    GetAllData(info);
    if (IsNoSourceData())
        return;

    int upperBound = GetUpperBound();
    int rangeCount = GetRange();

    if (CopyNotCurrentlyPossible(_isDisconnected, _isHomogenous))
        return;

    DoAsyncDbCopy(info, upperBound, rangeCount, _isDisconnected, _isHomogenous);
}

Now this is a standard piece of code with several "short-circuit" returns in the code.  Short-circuiting is good, but usually only if it appears as the first instruction in the method, and only once.  Otherwise, refactoring the code (as above) is a bear.  Select a section of text to pull out (extract method), and if it contains a "return" or break, or anything like that, then you'll most likely get a complaint from your refactoring tool.  ReSharper 4 complains "Extracted block has exits to different points Proceed with refactoring?(sic)".  Besides the pain from lack of proper punctuation (just a typo, I'm sure), the pain of not being able to refactor when you want to is even worse.

However, you can refactor if you do some manual refactoring (and this is still easier than manually extracting the method).  Instead of:

    string targetDb = GetTargetDB(info);
    if (targetDb.Equals(string.Empty))
        return;

    ...

 

Try the following:

    string targetDb = GetTargetDB(info);
    if (targetDb.Equals(string.Empty))
        return;
   
else
   
{
       
...
    }

Then you can start using your refactoring tools like "Invert If"

    string targetDb = GetTargetDB(info);
    if (!targetDb.Equals(string.Empty))
   
{
       
...
    }
    e
lse
        return;

And "voila," your else can be tossed as cruft.  Do so with the other "if"s and yes, you have more indented blocks of code, but indented blocks of code can be easily refactored with "Extract Method."
 
Kick it! Digg it!  Categories: Agile | C# | Refactoring

(Wow - I'm 2 for 2 on PoShGen, links are fixed on this post, too.  I need unit tests for my blog!)

OK, I admit it, what was available in 0.0.1 was possible using Enterprise Templates (if those things are still around – haven't looked at them in YEARS), though PoShGen is MUCH faster at it. And now in PoShGen 0.0.2 you can add multiple files after the initial solution is created – a Model View Presenter (MVP) "triad" a-la the Presenter-First flavor of MVP (and their respective test classes into a different project, at the same time).

Right now PoShGen is only a set of PowerShell functions, not a true PowerShell script – so you have to either "run" the script to put the functions in memory or what I do – copy and paste the script (via a simple right-click in the PowerShell console window), which works pretty well for me currently. I'm sure eventually things will get to be too big to copy & paste comfortably, but by then I'll probably be further along in Windows PowerShell in Action and can write a decent script.

Look at the last lines of the PoShGen.ps1 file to get an idea about how to call the two main PoShGen functions, NewApp and AddMvp.

Future plans for PoShGen include:

  • Adding an individual Model->Presenter->View event sequence to an existing MVP triad
  • Adding new Class/TestClass pairs to the solution
  • Other things as I run into them, in an attempt to remain DRY
  • Features requested by YOU, dear reader! Let me know what you think by either commenting below or send me an email at dseelinger@gmail.com

Don't forget to download your copy of PoShGen 0.0.2 here.


 
October 3, 2007
@ 08:54 PM

I'm really excited about the new functionality in C# 3.0. With all the great extensions to the C# language, writing DSL-like code is going to be easier than ever. And writing DSL is the key to writing intentional code that your customer (whoever that may be) will more likely understand, appreciate, and use as the excuse to keep you on for the next project.

However, there are some issues with Orcas that are preventing me from going all out with it as my personal environment. Lack of proper ReSharper 3.0 support for Orcas is a big barrier for me. Sure, they say they have Orcas support, but only when working with non .NET 3.5 features. Place some C# 3.0 specific code in your class and ReSharper lights up Orcas like a Christmas Tree – errors detected everywhere, though you can compile just fine. ReSharper's parser just can't handle C# 3.0 yet. I'm sure it will eventually. So why can't I live without ReSharper? I'm sure I could, but it would be like the rest of the MS developer world going back to the pre-Intellisense days. There are simply too many modern conveniences built into ReSharper. I could turn off ReSharper syntax highlighting, but that disables about half of the useful features of ReSharper (in addition to simply syntax highlighting).

Besides weak ReSharper support in Orcas, another problem I have is that my ISP does not yet support .NET 3.5. Granted, not many do, but this limits me to posting source code. I can't write anything cool and run it on my site. If anyone knows of any FREE .NET 3.5 sites out there, let me know. I've already pushed the WAF (Wife Acceptance Factor) on my current ISP spending. Hmm… Note to self: maybe it's time to start accepting donations.

So there you have it – yes, I will continue to "play" with Orcas, but serious work, either professionally or in my own spare time, will remain in the .NET 2.0 world. Maybe I can keep "upgrade" in mind as I write my "donation site" code in .NET 2.0.


 
September 7, 2007
@ 08:50 PM

Have you ever had a moment while working with the .NET Framework when you thought, "Hey, they forgot a method in this class!"  For me, that was in the beta for 1.0 when I was working with a lot of delimited text.  The String class had a nice Split() method, but the method only accepted a single character as the delimiter.  Why in the world would anyone ever use more than a single character to delimit text?  Oh, wait.  What about quoted strings?  There are other possibilities as well.  So I was forced to explore the possibility of extending the String class.  Should be simple enough, just inherit from String and off we go.  Then I discovered the sealed keyword.  If I wanted to extend string now, I would have to wrap every string method and property in another class and add my own Split method to that.  On second thought... nah.  Too painful.

I sure wish C# 3.0 had been around in those days - or is that an oxymoron.  Maybe, kind of.  But it sure would have been good to have Extension Methods back then.  I've read about extension methods in C# 3.0 more than once, but it never really dawned on me how something like this could be used until the other day, and then I realized a few ways it could be useful.  So how do you add the functionality into a sealed class that MS forgot?  Like so:

namespace TestProject1
{
    public static class Extension1
    {
        public static string[] Split(this string theString, string theDelimiter)
        {
            //...functionality goes here.
        } 
    }

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void SplitTest()
        {
            Assert.AreEqual(3, "My--three--words".Split("--").Length);
        }
    }
}

Simple enough.  I know, MS fixed it in a later framework, but it was a pain at the time.  And before you say, "You could have fixed this with a utility class Utility.Split(myString, myDelimiter)."  Yes, that's what I ended up doing in those situations, but it sure isn't as clean as saying String.Split()String should have control over its own fate, not be imposed upon by utility classes - at least it should look that way.  "Looking the way it should" has something to do with Domain Specific Languages.  Essentially with Extension Methods you could code something that is immensely more readable and closer to your target domain than the general language or framework.  For example, if your particular target domain often needs to calculate a certain amount of time-lapsed, then it could be done like: 20.Minutes().Ago().  That's very intentional and much more readable than DateTimeUtil.CalculateTimeInPast(TimeSpanUtil.ToMinutes(20));

namespace TestProject1
{
    public static class Extension2
    {
        public static TimeSpan Minutes(this int numMinutes)
        {
            return new TimeSpan(0, numMinutes, 0);
        }

        public static DateTime Ago(this TimeSpan duration)
        {
            return DateTime.Now.Add(-(duration));
        }
    }

    [TestClass]
    public class UnitTest2
    {
        [TestMethod]
        public void DomainSpecificLanguageTest()
        {
            Assert.IsTrue(20.Minutes().Ago() < DateTime.Now);
        }
    }
}

Full disclosure: I didn't come up with the 20.Minutes().Ago() example, I think I heard it from Scott Hanselman, though I didn't grok it at the time, not until I saw Extension Methods one more time. I'm going to start looking at problems like this with a fresh set of eyes now.