Improve your C# with IronRuby

You may have read that title and thought that this was going to just be another one of those “learn a different paradigm to make you think different” posts. Well, it isn’t. Those posts aren’t bad, and I have made a few of them, but this is a much less creative post…

Just to get this out of the way, all of the following code is covered under the MS-PL license.

If you haven’t had the luxury of getting to play around with the Ruby libraries then you are missing out. In fact, if you haven’t ever used them, then go check out some of the classes in the library to see just how powerful it really can be. For example, check out the String class in Ruby and you will see a list of methods that looks like this:

%   *   +   <<   <=>   ==   =~   []   []=   bytes   bytesize   capitalize   capitalize!   casecmp   center   chars   chomp   chomp!   chop   chop!   concat   count   crypt   delete   delete!   downcase   downcase!   dump   each   each_byte   each_char   each_line   empty?   end_with?   eql?   gsub   gsub!   hash   hex   include?   index   initialize_copy   insert   inspect   intern   length   lines   ljust   lstrip   lstrip!   match   new   next   next!   oct   partition   replace   reverse   reverse!   rindex   rjust   rpartition   rstrip   rstrip!   scan   size   slice   slice!   split   squeeze   squeeze!   start_with?   strip   strip!   sub   sub!   succ   succ!   sum   swapcase   swapcase!   to_f   to_i   to_s   to_str   to_sym   tr   tr!   tr_s   tr_s!   unpack   upcase   upcase!   upto

There is a lot of awesome functionality in there which could be really useful in C#. If only we had some code in C# that performed all of these actions! Oh wait, IronRuby is written in C#! We can just look up the IronRuby methods in the source.

So, how do we find these methods? Well, the IronRuby team has two attributes that help us find the method that we are looking for. One is called the “RubyClassAttribute” and the other is called “RubyMethodAttribute”. If we look in the IronRuby source and search for “chomp” we will actually find a few method that look like this:

[RubyMethod("chomp")]
public static MutableString/*!*/ Chomp(CodeContext/*!*/ context, MutableString/*!*/ self) {
    return InternalChomp(context, self, RubyUtils.GetExecutionContext(context).InputSeparator);
}

[RubyMethod("chomp")]
public static MutableString/*!*/ Chomp(CodeContext/*!*/ context, MutableString/*!*/ self, [NotNull]MutableString/*!*/ separator) {
    return InternalChomp(context, self, separator);
}

[RubyMethod("chomp")]
public static MutableString/*!*/ Chomp(CodeContext/*!*/ context, MutableString/*!*/ self, object separator) {
    return separator == null ? self : InternalChomp(context, self, Protocols.CastToString(context, separator));
}

So, here you can see that all of these methods are calling out to another method to do their bidding called “InternalChomp”. We were lucky in that the string class is the only class which has a “chomp” method, but if we had multiple type then that is where the “RubyClassAttribute” can help you out. If we look at the top of the class that we found the “Chomp” methods in we will see this attribute declaration:

[RubyClass("String", Extends = typeof(MutableString), Inherits = typeof(Object))]
public class MutableStringOps {

This is pretty self explanatory. This class represents methods that are on the Ruby “String” class, which is represented by the CLR type MutableString. If we go back to the “InternalChomp” method that we saw earlier, then we will see this code:

private static MutableString InternalChomp(CodeContext/*!*/ context, MutableString/*!*/ self, MutableString separator) {
    if (separator == null)
        return self;

    // Remove multiple trailing CR/LFs
    if (separator.Length == 0) 
        return RubyUtils.FlowTaint(context, self, ChompTrailingCarriageReturns(self, false));

    // Remove single trailing CR/LFs
    MutableString result = RubyUtils.FlowTaint(context, self, CreateSubClass(context, self, MutableString.Create(self)));
    int length = result.Length;
    if (separator.Length == 1 && separator.GetChar(0) == '\n') {
        if (length > 1 && result.GetChar(length - 2) == '\r' && result.GetChar(length - 1) == '\n') {
            result.Remove(length - 2, 2);
        } else if (length > 0 && (self.GetChar(length - 1) == '\n' || result.GetChar(length - 1) == '\r')) {
            result.Remove(length - 1, 1);
        }
    } else if (EndsWith(result, separator)) {
        result.Remove(length - separator.Length, separator.Length);
    }

    self.Version++;
    return result;
}

While this method may not be particularly interesting there is a lot of code you can go through in the IronRuby codebase to give you plenty of ideas. For example, everyone complains about C#’s lack of a good Range class. So why not go into the IronRuby source and get a few ideas for implementing your own C# Range class? So, go get the source for IronRuby, play around with it, and let me know what you find.

Be Sociable, Share!

Leave a comment