[Update on 2008-01-06]

I’ve modified the post to reflect the information Dino provided. It turns out the SpecialName attribute is not required on extension methods but only when you are defining normal extension methods only when defining new operators.

 

One of my projects implemented in C# makes frequent use of extension methods. Recently I started using IronPython to script that project and what I learned is that consuming those extension methods in C# is straightforward but with IronPython some extra work is involved.

Because it isn’t entirely obvious what one needs to do to make this work, I’ll use  this post to elaborate what I learned.

The diagram below summarizes it …

image

THE GOOD NEWS

  • It’s possible
  • You do NOT have to alter class A (“the extendee”)

THE BAD NEWS

  • manual and repetitive work is involved

STEP-BY-STEP INSTRUCTIONS TO MODIFY THE EXTENDING CLASS

1 - Add a reference to Microsoft.Scripting.Dll

In the Visual Studio project file for B, Add a reference to the Microsoft.Scripting.Dll file (this is found in the IronPython 2.0 install folder)

2 – Add an attribute that identifies that B extends A

image

Add this at the top of B’s source file, just after the using statements.

 

A REAL BEFORE vs. AFTER COMPARISON

In this example

  • we will add a Foo() method to a class we defined as well as to the string class (a.k.a System.String)
  • Both Foo() methods just return a string called Bar
  • The Dll that contains the extensionmethods is called “DemoIronPythonExtensionMethods.dll”
  • The role of B will be played by DemoExtensionClass
  • The role of A will be played by two classes – System.String and ClassToBeExtended

SOURCE CODE BEFORE

SOURCE CODE AFTER

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DemoIronPythonExtensionMethods
{
    public class ClassToBeExtended
    {
    }

    public static class DemoExtensionClass
    {
        public static string Foo(this string s)
        {
            return "Bar";
        }

        public static string Foo(this ClassToBeExtended c)
        {
            return "Bar";
        }
    }

}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

[assembly: Microsoft.Scripting.Runtime.ExtensionType(
    typeof(DemoIronPythonExtensionMethods.ClassToBeExtended),
    typeof(DemoIronPythonExtensionMethods.DemoExtensionClass)
    )]

[assembly: Microsoft.Scripting.Runtime.ExtensionType(
    typeof(string),
    typeof(DemoIronPythonExtensionMethods.DemoExtensionClass)
    )]
 

namespace DemoIronPythonExtensionMethods
{
    public class ClassToBeExtended
    {
    }

    public static class DemoExtensionClass
    { 
        public static string Foo(this string s)
        {
            return "Bar";
        }

        public static string Foo(this ClassToBeExtended c)
        {
            return "Bar";
        }
    }

}

   

WHAT HAPPENS IN IRONPYTHON 2 BEFORE

WHAT HAPPENS IN IRONPYTHON 2 AFTER

>>> import clr
>>> import System
>>> clr.AddReference("DemoIronPythonExtensionMethods.dll")
>>> import DemoIronPythonExtensionMethods
>>> c1 = DemoIronPythonExtensionMethods.ClassToBeExtended()
>>> c1.Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'ClassToBeExtended' object has no attribute 'Foo'

>>> s1= "HELLO WORLD"
>>> s1.Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'Foo'

>>> import clr
>>> import System
>>> clr.AddReference("DemoIronPythonExtensionMethods.dll")
>>> import DemoIronPythonExtensionMethods
>>> c1 = DemoIronPythonExtensionMethods.ClassToBeExtended()
>>> c1.Foo()
'Bar'
>>> s1= "HELLO WORLD"
>>> s1.Foo()
'Bar'

PARTING THOUGHTS

  • I it is a bit of a chore to annotating once per extending class.
  • I hope this becomes easier in future IronPython releases
  • I wonder if one could use an Aspect-Oriented Programming Tool like PostSharp to automate the task.