Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Last active November 18, 2022 18:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amirrajan/003af42df63302adbf782bae66ebad4f to your computer and use it in GitHub Desktop.
Save amirrajan/003af42df63302adbf782bae66ebad4f to your computer and use it in GitHub Desktop.
The power of C#, hindered by its culture and community.

The Power of C#, Hindered by Its Culture and Community

This is a demonstration of the power of C# from over a ten years ago. This really powerful capability is not used by .Net devs... for reasons.

The Duality of C# Explained

Imagine a C# type that has the capablity of being both statically typed and dynamically typed. We'll call this dual-type: Gemini.

Creating a Typeless Entity on the Fly

Here is a simple way you can use Gemini to create a dynamic object.

//create an object that has a FirstName and LastName property
dynamic gemini = new Gemini(new { FirstName = "Jane", LastName = "Doe" });

you can add properties to this entity through simple assignment:

gemini.MiddleInitial = "J";

ToString

Gemini's ToString() will give you its entire object graph. An example:

this (Gemini):
    FirstName: "Jane"
    LastName: "Doe"
    MiddleInitial: "J"

In the immediate window for Visual Studio, you can get this nice output using this line (observe the ",nq"):

gemini.ToString(),nq

RespondsTo

This is a really helpful method that you should get familiar with. An example:

gemini.RespondsTo("FirstName"); //this would return true
gemini.RespondsTo("IDontExist"); //this would return false

Adding Methods on the Fly

You can add methods on a Gemini if you are so inclined:

gemini.SayHello = new DynamicFunction(() => "Hello");
//calling method
gemini.SayHello();

You can add methods that take in parameters, with the added benefit of flexible invocation:

gemini.SayHello = new DynamicFunctionWithParam(d => "Hello " + d.NickName);
//calling method
gemini.SayHello(NickName: " Slick");
//or
gemini.SayHello(new { NickName = "Slick" });

Introspection

Here are a few more things you can do with Gemini.

gemini.SetMember("FirstName", "New First Name");
gemini.GetMember("LastName");
var subset = gemini.Exclude("LastName");
var anotherSubset = gemini.Include("FirstName", "MiddleInitial");
gemini.SetMember(new { FirstName = "New New First Name", LastName = "New Last Name" });
gemini.DeleteMember("FirstName");

Here is how you can get all the re-definable members on Gemini:

IDictionary<string, object> members = gemini.Hash();
//other options include
IDictionary<string, object> props = gemini.HashExcludingDelegates();
IEnumerable<string, object> methods = gemini.Delegates();

Gemini is really malleable, and incredibly powerful.

The .Net Framework comes with a dynamic object called ExpandoObject. This class inherits from System.DynamicObject which gives it some interesting capabilities. Here is an example:

//instantiate an instance of ExpandoObject
//but use the dynamic keyword
dynamic person = new ExpandoObject();

//add properties on the fly. cool
person.FirstName = "Jane";
person.LastName = "Doe";

//add methods on the fly. also cool
person.SayHello = new Func<string>(() =>
{
    return person.FirstName + " " + person.LastName + " says hello";
});

//call dynamic methods using dynamic properties
//this would print out "Jane Doe says hello";
Console.WriteLine(person.SayHello());

It's a pretty nifty little class. Wouldn't it be cool if you could inherit from ExpandoObject and add the ability to take on properties and methods to your own class? Well...you cant. Keep in mind that ExpandoObject doesn't contain any compile time properties, which is why you have to use the dynamic keyword. If you used the typed version of ExpandoObject, you would get compiler errors when setting the FirstName, LastName and SayHello members.

There are some benefits to attaching methods and properties to your own classes dynamically. Let's use a class that I've created and explore the dynamic capabilities of C#. We'll start with saying hello and build up to some very interesting examples:

//class inerhits from gemini, a construct
//that gives you dynamic awesomeness
public class Person : Gemini
{
    //base constructors for setting initial values
    //these constructors are optional constructors
    //you can use in your own objects to
    public Person(object dto) : base(dto) { }
    public Person() : this(new { }) { }

    //dynamic method 1 (no access modifier)
    dynamic SayHello()
    {
        return _.FirstName + " " + _.LastName + " says hello";
    }

    //dynamic method 2 (no access modifier)
    dynamic SayHelloTo(dynamic otherPerson)
    {
        return _.FirstName + " " + _.LastName +
               " says hello to " + otherPerson.FirstName +
               " " + otherPerson.LastName;
    }
}

//initialize instances
dynamic person1 = new Person(new { FirstName = "Jane", LastName = "Doe" });
dynamic person2 = new Person(new { FirstName = "John", LastName = "Doe" });

//call dynamic methods
//prints "Jane Doe says hello"
Console.WriteLine(person1.SayHello());

//prints "Jane Doe says hello to John Doe"
//you can pass in another object that responds to
//the properties FirstName, LastName
Console.WriteLine(person1.SayHello(person2));

//prints "Jane Doe says hello to Name Parameter Person"
//you can pass in named parameters which gets turned
//into a dynamic object for you
Console.WriteLine(person1.SayHello(
    FirstName: "Named Parameter",
    LastName: "Person"));

//also returns "Jane Doe says hello to Anonymous Person"
//you can pass in an anonymous type too
Console.WriteLine(person1.SayHello(new
{
    FirstName = "Anonymous",
    LastName = "Person"
}));

Some subtle things to note:

  • inherit from Gemini (it's the class that implements dynamic features exposed via System.DynamicObject)
  • methods that adhere to a certain signature are implicitly public (no need for access modifiers)
  • no need to create auto-properties, any dynamic member on a class can be accessed via the _ property
  • the SayHelloTo method can take in parameters in many different forms, you'll notice that all the variations work because the objects passed in all respond the FirstName and LastName properties

We are again using the dynamic keyword. The Person class doesn't have any compile time properties defined (or public methods for that matter). We use the dynamic keyword to bypass the compiler (otherwise we would get compiler errors). This allows us to create properties and methods on the fly during runtime.

Adding Members on the Fly

Now that you have an understanding of the general mechanics of Gemini (how to access properties, how to call dynamic methods). Let's do something crazy. We'll redefine what SayHelloTo does, ninja style. We can add this line to some entry point to our application:

//some entry point to our application such as
//the first line of static void Main() or
//the application start of a global.asax.cs

//for all instances of Person
Gemini.Extend<Person>(person =>
{
    //redefine the SayHelloTo method to "casually" say
    //hello to another person as opposed to its default
    //behavior
    person.SayHelloTo = new DynamicFunctionWithParam(otherPerson =>
    {
        return person.FirstName + " says a casual hello to " + otherPerson.LastName;
    });
});

With the line above. All instances of Person now have a SayHelloTo method that "casually" says hello as opposed to its original behavior. The ability to surgically replace and add methods gives you a lot of power.

You can also change the implementation on the fly for a single instance, it's useful when isolating components under test. Here is an example:

dynamic person = new Person(new { FirstName = "Jane", LastName = "Doe" });

//redefine a method without the need of a mocking framework
//as long as the method was defined with out an access modifier
//and returns/takes in a dynamic parameter it is automatically
//redefinable
person.SayHelloTo = new DynamicFunctionWithParam(otherPerson =>
{
    return "Mock hello to " + otherPerson.LastName;
});

//prints "Mock hello to Jane Doe"
Console.WriteLine(person.SayHello(FirstName: "Jane", LastName: "Doe"));

Gemini is a construct that inherits from DynamicObject, a class in the .Net 4.0 Base Common Library. DynamicObject paired up with the dynamic keyword gives us the ability to intercept pretty much any call made to an object. Gemini uses this interception capability to hook up and execute runtime bound methods like the ones defined above. Here are just a few of the methods you have at your disposal if you inherit from System.DynamicObject:

  • TryGetMember: this method is invoked if an object is attempting to access a property that doesn't exist
  • TrySetMember: this method is invoked if an object is attempting to set a property that doesn't exist
  • TryInvokeMember: this method is invoked if an object is attempting to execute a method that doesn't exist

Do Something Useful

All the examples so far have been trivial (I wanted to ease you into this new paradigm). Here is an example where we surgically add a method that does HTML formatting:

//for all instances of Person
Gemini.Extend<Person>(instance =>
{
    //get a list of all properties through one of Gemini's introspection methods
    var members =
        (instance.HashOfProperties() as IDictionary<string, object>).ToList();

    //foreach property, add a method on the fly that
    //ends in the word Html and returns the Html encoded property
    members.ForEach(kvp =>
    {
        //add the method to this instance using Gemini's SetMember method
        instance.SetMember(
            kvp.Key + "Html",
            new DynamicFunction(() =>
            {
                return HttpUtility.HtmlEncode(instance.GetMember(kvp.Key));
            });
    });
});

dynamic person = new Person(new { FirstName = "Jane<>", LastName = "Doe<>" });

//this would print Jane&lt;&gt;
Console.WriteLine(person.FirstNameHtml());

//this would print Doe&lt;gt;
Console.WriteLine(person.LastNameHtml());

Now, any instance of Person will automatically get an Html method for each property. Any class that inherits from Person would also get this capability.

I hope you are starting to see some of the benefits of using the dynamic keyword in conjunction with classes that inherit from DynamicObject. If you were to create this kind of capability with strict static typing, you would have to define a method for every property on every class. Because we have bypassed the compiler, we can use a convention based approach and dynamically invoke these methods instead.

Modules

Gemini's Extend method can take in a delegate to do some light changes to objects. You can create modules to do more of the heavy lifting. Here is the same HTML capability but as a module:

//module definition
public class HtmlFormatting
{
    dynamic instance;

    //modules are defined by create a class that takes in
    //one constructor argument (the dynamic entity to attach
    //behavior to)
    public HtmlFormatting(dynamic gemini)
    {
        //store gemini for later use in the module if needed
        instance = gemini;

        //get a list of all properties on the dynamic instance
        var members =
            (gemini.HashOfProperties() as IDictionary<string, object>)
            .ToList();

        //add the Html method for each property
        members.ForEach(kvp =>
        {
            gemini.SetMember(kvp.Key + "Html",
              new DynamicFunction(() => HtmlEncode(kvp.Key)));
        });
    }

    dynamic HtmlEncode(string key)
    {
        return HttpUtility.HtmlEncode(instance.GetMember(key));
    }
}

//at some entry point of the application,
//extend all instances of Person with HtmlFormatting
Gemini.Extend<Person, HtmlFormatting>()

//Extra Credit:
//if you have other modules defined,
//you can extend an object with those also (a form of multiple inheritence)
Gemini.Extend<Person, ModuleA>()
Gemini.Extend<Person, ModuleB>()

There are some inherit benefits to using modules like this. During design time, you can independently test each module and then "mix" them all together during run time. It leads to a very component based approach where an object gains capabilities at different layers. jQuery is a perfect example of a module based approach, you download a plug-in, which get's mixed into all jQuery object...instant awesomeness!

Being able to mix in different modules gives you a form of multiple inheritence. You can have independent components that all perform a specific task and then mix these capabilities into your objects at runtime. With static C#, you are limited to extension methods and interfaces (and the contraints of the compiler).

Lazy

With dynamic typing, you can lazily add methods to a class. Here is another way you can add HTML capabilities to a class on demand.

public class Person : Gemini
{
    //define a method called MethodMissing taking in one parameter
    dynamic MethodMissing(dynamic callInfo)
    {
        //check to see if the method being called ends in Html
        if(callInfo.Name.EndsWith("Html"))
        {
            //if it does, add a method on the fly that returns
            //the html encoded version of the parsed property
            var member = callInfo.Name.Replace("Html", "");
            SetMember(callInfo.Name,
                new DynamicFunction(() =>
                {
                    return System.Web.HttpUtility.HtmlEncode(GetMember(member));
                });

            //return the value of the method
            return GetMember(callInfo.Name)();
        }

        //if the pattern isn't there, throw an exception defined in Gemini
        throw (Exception)MemberDoesntExistException(callInfo.Name);
    }
}

And here is the usage:

dynamic person = new Person();

person.FirstName = "Jane<>"; //set the FirstName Property
person.FirstName; //this would return Jane<>

/*
attempt to call FirstNameHtml()
calling this the first time will execute the
method missing block with the following callInfo
this (Gemini)
  Name (String): FirstNameHtml
    Parameters (IEnumerable<dynamic>)
    ParameterNames (IEnumerable<dynamic>)
  Instance (Person)
    MethodMissing (DynamicFunctionWithParam)
    SetMembers (DynamicFunctionWithParam)
    FirstName (String): Jane<>
  Parameter (Gemini)
    SetMembers (DynamicFunctionWithParam)
  SetMembers (DynamicFunctionWithParam)
*/
//this would print Jane&lt;&gt;
Console.WriteLine(person.FirstNameHtml());

//method missing will not get called again because now the method exists :-)
//this would print Jane&lt;&gt;
Console.WriteLine(person.FirstNameHtml());

Keep in mind that dynamic objects just need to have a method before invocation. The method missing on Person could have also been defined like this:

//an empty Person class
public class Person : Gemini
{
}

//at some entry point in the application such as static void main
//or global.cs.asax
Gemini.Extend<Person>(person =>
{
    person.MethodMissing = new DynamicFunctionWithParam(callInfo =>
    {
        if (callInfo.Name.EndsWith("Html"))
        {
            var member = callInfo.Name.Replace("Html", "");
            callInfo.Instance.SetMember(callInfo.Name,
                new DynamicFunction(() =>
                {
                    return System.Web.HttpUtility.HtmlEncode(
                        callInfo.Instance.GetMember(member)))
                });
            return callInfo.Instance.GetMember(callInfo.Name)();
        }

        throw (Exception)callInfo.Instance.MemberDoesntExistException(callInfo.Name);
    });
});

I can't stress enough how many incredible things can be done with the ability to manipulate objects on the fly. Now we can have classes that create methods only when the need them...the dynamic keyword has afforded us this capability :-)

Another Example

Let's take this module pattern for a test drive and see how a class can quickly gain some powerful capabilities. Let's say I have a module called Validations. We'll take a UserRegistration class and see how the object morphs as this module is mixed in:

//a plain jane class opting into dynamic awesomeness
public class UserRegistration : Gemini
{
    public UserRegistration(object dto) : base(dto) { }
    public UserRegistration() : this(new { }) { }
}

dynamic userRegistration = new UserRegistration(new
{
    Email = "user@example.com",
    Password = "Secret"
});

No modules have been mixed in at this point. Performing a ToString() on this object will print this out:

this (UserRegistration)
  Email (String): user@example.com
  Password (String): Secret

Now lets include Validations:

public class UserRegistration : Gemini
{
    public UserRegistration(object dto) : base(dto) { }
    public UserRegistration() : this(new { }) { }

    //we can define this at the entry point of our application
    //or just put the module definitions into a static constructor
    static UserRegistration()
    {
        Gemini.Extend<UserRegistration, Validations>();
    }

    //defining a method called validates gives an object validation capabilities,
    //if you don't define this method, the validations module simply ignores
    //the mixin and won't attach validation methods to your object
    IEnumerable<dynamic> Validates()
    {
        //verify that the email matches a specfic format
        yield return
        new Format("Email")
        {
            With = @"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$"
        };

        //verify that passwords must match
        yield return
        new Confirmation("Password") { ErrorMessage = "Passwords must match." }
    }
}

With the Validations module mixed in, performing a ToString() on this object will print this out:

this (UserRegistration)
  Validates (DynamicFunction)
  Errors (DynamicFunction)
  IsValid (DynamicFunctionWithParam)
  FirstError (DynamicFunction)
  Email (String): user@example.com
  Password (String): user@example.com
  PasswordConfirmation: null

The validations module added properties and methods for us without the need for inheritance. Specifically, we stated that the password requires confirmation. Given that, the validation rule added the PasswordConfirmation property for us. Here is a sample of using the validations module:

//all dynamically typed so we bypass the compiler
dynamic userRegistration = new UserRegistration(new
{
    Email = "user@example.com",
    Password = "Secret"
});

//this would return false
userRegistration.IsValid();

//this would return "Passwords must match."
userRegistration.FirstError();

//set the password to match
userRegistration.PasswordConfirmation = userRegistration.Password;

//this would return true
userRegistration.IsValid();

userRegistration.Email = "i am invalid";

//this would return false
userRegistration.IsValid();

//this would return "Email is invalid."
userRegistration.FirstError();

Yet another example

Ever wanted to cache the values returned from long running methods? Here is an example of a module called Memoize.

public class SlowPerson : Gemini
{
    dynamic SayHello(dynamic otherPerson)
    {
        //wait an uncomfortable 10 seconds and then say hello
        System.Threading.Thread.Sleep(10000);

        return "Hello " + args.Name;
    }
}

Now lets memoize the long running SayHello method using a module:

public class SlowPerson : Gemini
{
    //we can define this at the entry point of our application
    //or just put the module definitions into a static constructor
    static SlowPerson()
    {
        Gemini.Extend<SlowPerson, Memoize>();
    }

    IEnumerable<dynamic> Memoize()
    {
        //define a method called Memoize
        //and state that you want to memoize the SayHello
        //method
        yield return (DynamicFunctionWithParam)SayHello;
    }

    dynamic SayHello(dynamic otherPerson)
    {
        //wait an uncomfortable 10 seconds and then say hello
        System.Threading.Thread.Sleep(10000);

        return "Hello " + otherPerson.Name;
    }
}

That's it. The SayHello method is now memoized. Here is the behavior of the memoized method:

dynamic slowPerson = new SlowPerson();

//takes 10 seconds
slowPerson.SayHello(Name: "Jane Doe");

//saying hello again returns immediately with the cached result
slowPerson.SayHello(Name: "Jane Doe");

//takes 10 seconds (different arguements)
slowPerson.SayHello(Name: "John Doe");

//saying hello again returns immediately with the cached result
slowPerson.SayHello(Name: "John Doe");

Take a moment and reflect on this module. With very little code, I was able to do a parameter by parameter cache for a method. It's hard to create something this elegant in a strongly typed world (I've searched around and the best I've seen is memoization at the functional level, if someone can think of a elegant solution I'll be happy to update this blog post with your implementation). Also, keep in mind that you can include multiple modules into any class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment