Polymorphism in C# Without Classes

DZone 's Guide to

Polymorphism in C# Without Classes

A developer explains how this is possible using his open source project, and gives some example code to help get you started.

· Web Dev Zone ·
Free Resource

Imagine all the benefits you'd experience if you could somehow apply polymorphism in C# without classes and methods. First of all, there would be far less dependencies in your code, no abstract base classes or interfaces that could potentially break your code in the future, no method signatures that could potentially break in the future, and no static types that could potentially break in the future. Sounds too good to be true? Read on my friend, and I'll show you the glory.


Believe it or not, but the above is actually a functioning piece of computer code. It is an invocation to an "Active Event" who's name if "foo.bar", and it takes two arguments; arg1 and arg2. Its C# implementation might look like the following.

using p5.core;

namespace foo {

  public static class Bar {

    [ActiveEvent (Name = "foo.bar")]
    public static void foo_bar (ApplicationContext context, ActiveEventArgs e)
      var arg1 = e.Args.GetChildValue ("arg1", context, "");
      var arg2 = e.Args.GetChildValue ("arg2", context, "");
      e.Args.Value = arg1 + " " + arg2;

What it returns I assume is obvious. However, after invocation the foo.bar "Node" will have the value "hello world." The first piece of code, that didn't really resemble code at all, is actually a relational file format called "Hyperlambda," which just so happens to create a relational Node tree structure, where a Node can have a name, a value, and a bunch of children nodes. Our root node above is called "foo.bar", and it has two children: arg1 and arg2. The values of our arg1 and arg2 nodes are respectively "hello" and "world." If we run the above Hyperlambda or Node structure through our "eval" Active Event, it will result in dynamically invoking our above "foo_bar" method, assuming we have registered the assembly where we declared our method as an "Active Event sink." If we later, for some reason, want to exchange our above C# implementation, we can easily do that, by, for instance, creating another assembly which implements this specific Active Event, and throw out our old assembly or method. The code will still happily accept our new Active Event, as if nothing had changed. This is true even if we change the "signature" of our method, since there are no signatures, only an expectation to a "bunch of nodes." So every single Active Event is arguably interchangeable, with any other Active Event - at least in theory. And a Node reference, is basically a "piece of execution instructions" which is 100% Turing Complete, allowing you to "eval" a bunch of nodes.

If you want to create yet another layer of abstraction, to inject inbetween your code, and the C# code, to facilitate for dynamic polymorphism, during runtime - you could accomplish that with the following Hyperlambda.


At this point, instead of invoking our "foo.bar" Active Event directly, we can invoke "foo.invoke-bar". The latter event will apply all arguments it is given, and invoke "foo.bar" with these arguments, and return the results to the caller. So, at this point, we have created an "adapter" Active Event, which we invoke instead of invoking our C# event directly, which, of course, is easily exchanged with whatever other C# Active Event you choose to create later down the road. In fact, the above is such a common scenario, we could easily create another Active Event, which simply takes one source Active Event as a string, and another destination Active Event as another string, and automatically create an adapter event like the one above - at which point our code would look like the following:


If we implemented the above "foo.create-adapter-event" in either C# or Hyperlambda, we could create such "adapter events" with simple three liners every time we needed to create an "interface event," which interfaces with our C# code. And every time we needed to exchange the implementation, we could simply re-invoke our above "foo.create-adapter-event" with another destination event (argument), and we would have dynamically applied polymorphism, in C#, without as much as a single compilation occuring, or even risking having other users simultaneously using our events being in different threads being affected - however, all future invocations to "foo.invoke-bar" for each new ApplicationContext instance, would, instead, point to our new "override."

So, all of a sudden we have a dynamic "scripting language" we can combine with C#, to create a dynamic execution environment, allowing us to create a much richer and more flexible plugin architecture, than any amount of C# could possibly ever give us. With the ability to easily apply polymorphism on functionality, without even having to take down our (web) server, or affecting users already consuming our service. And every single method in your solution has been completely "detangled," and are now loosely coupled pieces of functionality, allowing you to juggle your methods as "independent loosely coupled pieces of encapsulated functionality," and exchange them as you see fit, with any other implementation you happen to have laying around. Basically, you have become a magician in loosely coupling, dynamic invocations, and polymorphism - and you have a dynamically interpreted scripting language for your C# solutions. Not bad for a 5 minute long read.

The above Active Event design pattern is implemented in Phosphorus Five. You can download Phosphorus Five here. Phosphorus Five is open source, but also comes with commercial proprietary offerings if you require additional support, or need to use it in closed source projects. Phosphorus Five is in its entirety created by me (the author of this article).

A 3 minutes long video demonstrating the process.

Download Phosphorus Five and play around with it.

c#, polymorphism, tutorial, web dev

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}