Cross-language Extension Method Calling
- by Tom Hines
Extension methods are a concise way of binding functions to particular types.
In my last post, I showed how Extension methods can be created in the .NET 2.0 environment.
In this post, I discuss calling the extensions from other languages.
Most of the differences I find between the Dot Net languages are mainly syntax. The declaration of Extensions is no exception. There is, however, a distinct difference with the framework accepting excensions made with C++ that differs from C# and VB.
When calling the C++ extension from C#, the compiler will SOMETIMES say there is no definition for DoCPP with the error:
'string' does not contain a definition for 'DoCPP' and no extension method 'DoCPP' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
If I recompile, the error goes away.
The strangest problem with calling the C++ extension from C# is that I first must make SOME type of reference to the class BEFORE using the extension or it will not be recognized at all. So, if I first call the DoCPP() as a static method, the extension works fine later. If I make a dummy instantiation of the class, it works. If I have no forward reference of the class, I get the same error as before and recompiling does not fix it. It seems as if this none of this is supposed to work across the languages.
I have made a few work-arounds to get the examples to compile and run.
Note the following examples:
Extension in C#
using System;
namespace Extension_CS
{
public static class CExtension_CS
{ //in C#, the "this" keyword is the key.
public static void DoCS(this string str)
{
Console.WriteLine("CS\t{0:G}\tCS", str);
}
}
}
Extension in C++
/****************************************************************************\
* Here is the C++ implementation. It is the least elegant and most quirky,
* but it works.
\****************************************************************************/
#pragma once
using namespace System;
using namespace System::Runtime::CompilerServices; //<-Essential
// Reference: System.Core.dll //<- Essential
namespace Extension_CPP {
public ref class CExtension_CPP
{
public:
[Extension] // or [ExtensionAttribute] /* either works */
static void DoCPP(String^ str)
{
Console::WriteLine("C++\t{0:G}\tC++", str);
}
};
}
Extension in VB
' Here is the VB implementation. This is not as elegant as the C#, but it's
' functional.
Imports System.Runtime.CompilerServices
'
Public Module modExtension_VB 'Extension methods can be defined only in modules.
<Extension()> _
Public Sub DoVB(ByVal str As String)
Console.WriteLine("VB" & Chr(9) & "{0:G}" & Chr(9) & "VB", str)
End Sub
End Module
Calling program in C#
/******************************************************************************\
* Main calling program
* Intellisense and VS2008 complain about the CPP implementation, but with a
* little duct-tape, it works just fine.
\******************************************************************************/
using System;
using Extension_CPP;
using Extension_CS;
using Extension_VB; // vitual namespace
namespace TestExtensions
{
public static class CTestExtensions
{
/**********************************************************************\
* For some reason, this needs a direct reference into the C++ version
* even though it does nothing than add a null reference.
* The constructor provides the fake usage to please the compiler.
\**********************************************************************/
private static CExtension_CPP x = null; // <-DUCT_TAPE!
static CTestExtensions()
{
// Fake usage to stop compiler from complaining
if (null != x) {} // <-DUCT_TAPE
}
static void Main(string[] args)
{
string strData = "from C#";
strData.DoCPP();
strData.DoCS();
strData.DoVB();
}
}
}
Calling program in VB
Imports Extension_CPP
Imports Extension_CS
Imports Extension_VB
Imports System.Runtime.CompilerServices
Module TestExtensions_VB
<Extension()> _
Public Sub DoCPP(ByVal str As String)
'Framework does not treat this as an extension, so use the static
CExtension_CPP.DoCPP(str)
End Sub
Sub Main()
Dim strData As String = "from VB"
strData.DoCS()
strData.DoVB()
strData.DoCPP() 'fake
End Sub
End Module
Calling program in C++
// TestExtensions_CPP.cpp : main project file.
#include "stdafx.h"
using namespace System;
using namespace Extension_CPP;
using namespace Extension_CS;
using namespace Extension_VB;
void main(void)
{
/*******************************************************\
* Extension methods are called like static methods
* when called from C++. There may be a difference in
* syntax when calling the VB extension as VB Extensions
* are embedded in Modules instead of classes
\*******************************************************/
String^ strData = "from C++";
CExtension_CPP::DoCPP(strData);
CExtension_CS::DoCS(strData);
modExtension_VB::DoVB(strData); //since Extensions go in Modules
}