The C# extern keyword does very little, it just tells the compiler that the method declaration won't have a body. The compiler does a minimum check, it insists that you provide an attribute as well, anything goes. So this sample code will compile just fine:
class Program {
static void Main(string[] args) {
foo();
}
class FooBar : Attribute { }
[FooBar]
static extern void foo();
}
But of course it will not run, the jitter throws its hands up at the declaration. Which is what is required to actually run this code, it is the jitter's job to generate proper executable code for this. What is required is that the jitter recognizes the attribute.
You can see this done in the source code for the jitter in the SSCLI20 distribution, clr/src/md/compiler/custattr.cpp source code file, RegMeta::_HandleKnownCustomAttribute() function. That's code that's accurate for .NET 2.0, I'm not aware of additions to it that affect method calling. You'll see it handling the following attributes that relate to code generation for method calls, the kind that will use the extern keyword:
[DllImport], you no doubt know it
[MethodImpl(MethodImplOptions.InternalCall)], an attribute that's used on methods that are implemented in the CLR instead of the framework. They are written in C++, the CLR has an internal table that links to the C++ function. A canonical example is the Math.Pow() method, I described the implementation details in this answer. The table is not otherwise extensible, it is hard-baked in the CLR source code
[ComImport], an attribute that marks an interface as implemented elsewhere, invariably in a COM server. You rarely program this attribute directly, you'd use the interop library that's generated by Tlbimp.exe instead. This attribute also requires the [Guid] attribute to give the required guid of the interface. This is otherwise similar to the [DllImport] attribute, it generates a pinvoke kind of call to unmanaged code but using COM calling conventions. This can of course only work properly if you actually have the required COM server on your machine, it is otherwise infinitely extensible.
A bunch more attributes are recognized in this function but they don't otherwise relate to calling code that's defined elsewhere.
So unless you write your own jitter, using extern isn't a viable way to get what you want. You could consider the Mono project if you want to pursue this anyway.
Common extensibility solutions that are pure managed are the largely forgotten System.AddIn namespace, the very popular MEF framework and AOP solutions like Postsharp.