Derek Slager

  • Saturday, October 21, 2006

    Channeling Ruby in C# 3.0

    I happened across a posting earlier today which contrasted Java and Ruby code snippets. The intent of the post (an enjoyable read, by the way) was to demonstrate the elegance of Ruby's simple, compact syntax, using the more verbose Java as a counter-example.

    This post, on the other hand, attempts to use some of C#'s more recent features to demonstrate how static languages are closing the "elegance" gap (for lack of a better term) which the original poster demonstrated.

    Extension Methods

    The author opens with this example:

    10.times { print "ho" }

    The author of the post implies that this is possible because Ruby treats 10 as an object, while Java does not. The same is true in C# (integers do not inherit from Object, and are "primitive"); however, with the help of extension methods in C# 3.0 I can very closely emulate Ruby's syntax:

    10.Times(i => Console.Write("ho"));

    The integer here is never boxed -- this is just syntactic sugar for explicitly calling the extension method with an explicit argument. Here's the entire source of the extension method:

    public static void Times(this int value, Action<int> action) {
        for (int i = 0; i < value; i++) {
            action(i);
        }
    }

    The extension method syntax is a C# 3.0 specific feature, but is callable from .NET 2.0 code (and in fact, runs on the 2.0 CLR) using the more explicit syntax:

    RubyishExtensions.Times(10, delegate(int) {
        Console.Write("ho");
    });

    The author lists several other examples which are easily replicated using C# 3.0 extension methods. Looking for an odd number?

    if 11.odd? print "Odd!"

    In C# 3.0:

    if (11.Odd()) Console.Write("Odd!");

    The relevant extension method:

    public static bool Odd(this int value) {
        return value % 2 != 0;
    }

    Counting bytes?

    102.megabytes + 24.kbytes + 10.bytes

    In C# 3.0:

    102.Megabytes() + 24.Kilobytes() + 10.Bytes();

    Three very simple extension methods help here:

    public static int Megabytes(this int value) {
        return value * 1024 * 1024;
    }
    
    public static int Kilobytes(this int value) {
        return value * 1024;
    }
    
    public static int Bytes(this int value) {
        return value;
    }

    How about ordinalizing a number?

    print "Currently in the #{2.ordinalize} trimester"

    Again, in C# 3.0:

    Console.Write("Currently in the {0} trimester",
                  2.Ordinalize());

    This extension method is a little longer -- but, like all extension methods, it's reusable:

    public static string Ordinalize(this int value) {
    
        string ordinalized;
    
        if ((value % 100).Between(11, 13)) {
            ordinalized = "th";
        } else {
            switch (value % 10) {
            case 1:
                ordinalized = "st"; break;
            case 2:
                ordinalized = "nd"; break;
            case 3:
                ordinalized = "rd"; break;
            default:
                ordinalized = "th"; break;
            }
        }
    
        return value + ordinalized;
    }
    
    public static bool Between(this int value, int min, int max) {
        return value >= min && value <= max;
    }

    Our extension method fun ends with a few date examples:

    puts "Running time: #{1.hour + 15.minutes + 10.seconds} seconds"
    20.minutes.ago
    20.minutes.until("2006-10-9 11:00:00".to_time)

    In C# 3.0:

    Console.Write("Running time: {0} seconds",
                  (1.Hours() + 15.Minutes() + 10.Seconds()).TotalSeconds);
    20.Minutes().Ago();
    20.Minutes().Until(DateTime.Parse("2006-10-9 11:00:00"));

    Predictably, it's extension methods to the rescue again:

    public static TimeSpan Hours(this int value) {
        return TimeSpan.FromHours(value);
    }
    
    public static TimeSpan Minutes(this int value) {
        return TimeSpan.FromMinutes(value);
    }
    
    public static TimeSpan Seconds(this int value) {
        return TimeSpan.FromSeconds(value);
    }
    
    public static DateTime Ago(this TimeSpan timeSpan) {
        return DateTime.Now - timeSpan;
    }
    
    public static DateTime Until(this TimeSpan timeSpan,
                                 DateTime target) {
        return target - timeSpan;
    }

    Properties

    The next example demonstrates a feature of Ruby's syntax which I cannot emulate -- its tight property syntax:

    class Circle
      attr_accessor :center, :radius
    end

    C# is a bit lighter than Java, but it's more finger effort here:

    class Circle {
        private Point center;
        private float radius;
        public Point Center {
            get { return this.center; }
            set { this.center = value; }
        }
        public float Radius {
            get { return this.radius; }
            set { this.radius = value; }
        }
    }

    Collections

    The next section of the post contrasts the collection syntax ... the author argues in favor of Ruby on a number of points here, none of which I wholeheartedly agree on (I prefer to be explicit with collection choice and usage).

    Initializer syntax is singled out for non-array types (fairly). This is another problem nicely solved in C# 3.0 by way of collection initializers. The following snippets both result in a strongly typed int array of 3 numbers, with the second array coming by way of a generic collection:

    new int[] { 1, 2, 3 };
    new List<int> { 1, 2, 3 }.ToArray();

    The author next argues for a more complete collection of methods on various collections, including dynamic array growth. Some of this could be accomplished with further extension methods, but I personally feel that Push and Pop belong on Stack, not Array, so I'm not going to bother implementing those.

    In terms of dynamic array growth, that is handled quite nicely by collection classes which are optimized for that case (List<T> et al). Array-like syntax is supported (as it has been since C# 1.0), but my personal preference is to call an Add method when I want to add something.

    File I/O and Regular Expressions

    The last language-level feature the author contrasts is parsing sentences containing the work "Ruby" from a text file:

    File.read('test.txt').scan(/.*?\. /).each {
        |s| puts s if s =~ /Ruby/
    }

    This is compact, to be sure, but we don't need any C# 3.0 tricks to get close:

    using (StreamReader reader = new StreamReader("test.txt")) {
        foreach (Match match in Regex.Matches(reader.ReadToEnd(), @"(.*?\.)\s+?")) {
            if (match.Value.Contains("Ruby")) {
                Console.WriteLine(match.Value);
            }
        }
    }

    Summary

    The point of this post was not to imply that C# is better than Ruby, or Java for that matter. I simply wanted to point out that brevity and elegance are not the exclusive domain of dynamic languages. "Syntactic sugar" can go a long ways, without requiring fundamental changes to a language or its runtime. C# 3.0 is a particularly relevant example, given that it runs atop the 2.0 CLR despite all of its syntactic sugar.

    If you're interested in toying around with the examples cited in the program listings above, I've attached the sample code to this post. To use it, install the LINQ CTP and run MSBuild from a command prompt.

    Attachments

    • Rubyish.zip
    9 CommentsPosted at 4:48 AM to Categories: C# .NET
  • Tuesday, October 17, 2006

    Reading Masked Input from the System Console with .NET 2.0

    In the .NET Framework 2.0, System's Console class was enhanced to provide a variety of new functionality. The ability to trap typed keys was introduced, as well as explicit control for cursor positioning. In a nutshell, managed developers were given just enough control over the system console to read masked input, which is what this post is about.

    The following sections contain the details of reading masked input (think passwords) from the console.

    Choosing a String Format

    Also introduced in the 2.0 version of the Framework was the SecureString class, which wraps a string buffer that is encrypted in memory and can be freed predictably. The downside is that it's a bit more complicated to use, and it is only usable in a small number of standard APIs (Process.Start being the most relevant). Still, it's the best choice for reading a password to be fed into one of the API methods which support it.

    It is also quite useful to obtain a normal System.String instance for other use cases. If you're collecting a password to send to an FTP server, for example, it's quite useless in the context of overall system security to trouble yourself storing the password encrypted in memory (the password is sent in clear text via FTP's control connection).

    Rather than limit the API to one or the other, I decided to add support for both. The code which collects input from the console operates using the following interface abstraction:

    private interface IBuffer {
        void InsertChar(int index, char c);
        void DeleteChar(int index);
        void Clear();
    }

    Concrete implementations of this interface are provided which wrap a SecureString instance as well as a StringBuilder. Listed below is the implementation for the former:

    private class SecureStringBuffer : IBuffer {
        private SecureString buffer;
        public SecureStringBuffer(SecureString buffer) {
            this.buffer = buffer;
        }
        public void InsertChar(int index, char c) {
            this.buffer.InsertAt(index, c);
        }
        public void DeleteChar(int index) {
            this.buffer.RemoveAt(index);
        }
        public void Clear() {
            this.buffer.Clear();
        }
    }

    Reading Console Input

    The only method used for receiving input from the console is Console.ReadKey. An overload of this method enables the caller to trap input -- that is, read a character without it being automatically echoed to the console by the framework. When the user presses a character key, for example, we append it to the buffer (via our IBuffer abstraction) and emit the mask character (*).

    The input handling code is rather uninteresting in long form, but here's a snippet to give you a general idea (the remainder is available in the full source code, attached):

    ConsoleKeyInfo keyInfo;
    while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Enter) {
        if (keyInfo.Key == ConsoleKey.Backspace) {
            if (position > 0) {
                buffer.DeleteChar(--position);
                Write(startPosition + --length, ' ');
            }
        } else if ...
    }

    I tried to emulate the basic behavior of the standard ReadLine method as much as possible -- that said, I'm sure I missed a few shortcuts which I don't happen to be aware of. If I missed something obvious, feel free to add a comment and I'll update the source.

    Using MaskedInputReader

    Using the MaskedInputReader is very simple. Here's a sample invocation which reads a SecureString (from Test.cs):

    Console.WriteLine("Enter current password: ");
    using (SecureString password = MaskedConsoleReader.ReadSecureLine()) {
        Process.Start("notepad.exe",
                      Environment.UserName,
                      password,
                      Environment.UserDomainName);
    }

    One of the primary motivations for building this library was to enable accepting credentials from MSBuild tasks. This is likely to be an increasingly common need with the release of Vista inspiring more developers to run LUA. A task to install an assembly to the GAC, for example, would require elevation if run by a LUA user.

    Summary

    The feature set is summarized below:

    • Support for multiple string types.
    • Backspace and delete handling.
    • Line buffer navigation via arrow keys or Home / End.

    The source code is attached to this post. It's MIT licensed, which means it can be used commercially provided the copyright stays in tact.

    Attachments

    • MaskedConsoleReader.zip
    1 CommentPosted at 5:08 AM to Categories: C# .NET

About

I'm a programmer at Appature, a Seattle-area startup building marketing-focused CRM tools.

Email Me

Recent

  • DVCS Myths
  • A DVCS Story
  • BCrypt.net - Strong Password Hashing for .NET and Mono
  • A Better .NET Regular Expression Tester
  • Emulating Vista's User Directory Structure on XP
  • Screencast: Formatting a CSS File with Emacs
  • Emacs Hack #3: Compile Emacs from CVS on Windows
  • Emacs Hack #2: Manage Emacs Instances with gnuserv
  • Emacs Hack #1: Install Emacs on Windows
  • The Case for Emacs

Categories

  • .NET
  • Baseball
  • C#
  • DVCS
  • Emacs
  • LINQ
  • Security
  • Windows
© Copyright 2008 Derek Slager