There is another use case for anonymous classes, even simpler than that of providing implementations of interfaces: Anonymous POCO’s.

We’re all familiar with the idea of declaring a class that identifies the members that all instances (objects) of that class have, and then creating instances of that class. Anonymous classes, as the name suggests, allows us to instead define the members of a class at the very moment that we instantiate the class.

In this case, the class is not identifiable by any name given it in the code.

A simple example might be drawn from C#/.net, when configuring a route in an ASP.NET MVC application:


   routes.MapRoute
   (
      "Default",
      "{controller}/{action}/{id}",
      new
      { 
         controller = "Home", 
         action     = "Index", 
         id         = ""
      }
   );

The formatting here is to keep everything easily in view in the source code presentation of the blog template and to draw attention to what is going on rather than indicate any preferred style.

The first two parameters in the call to MapRoute are clearly strings. But the third is rather strange. We have the new keyword but instead of being followed by a class identifier we then have what appears to be a statement block consisting of assignment statements separated by commas rather than statement terminators.

This is an example of an object instantiated in-place (or in-line if you prefer) but without reference to any identifiable class name. Hence “anonymous class” (though it isn’t really anonymous as we shall see).

There is nothing exotic about the parameter passing mechanism itself. The parameter to MapRoute involved is declared simply as object and so any object is legitimate, even one which is an instance of an anonymous class.

The specific anonymous class instantiated here in the C# will have three properties, controller, action and id, with the values as initialised. But we could have identified and initialized whatever member names we needed, to correspond to the values identified in the second parameter.

What’s the Point ?

For those not familiar with this Api, allow me to explain. Others can skip ahead to the Oxygene Approach, if you prefer.

The third parameter to the MapRoute call is an object that provides default values for the variable or templated components of the url provided in the second parameter (the variables are those parts of the url enclosed in { braces }). The names of the members in the object correspond to those url variables.

To match the properties of the default object to these named url variables the MapRoute method will (I presume) use reflection to discover the properties of the object, matching them up to those url values as required, by name.

Of course, a url template in an application may consist of any number of parts, and each application will have different names for those parts, so the MapRoutes Api cannot dictate a specific class with prescribed value names to be passed.

In this case the designers of the MapRoute Api could perhaps have used a dictionary<string, string> but anonymous classes are far more flexible and powerful than just providing a set of string/string name/value pairs.

But before we look at that, let’s look at the Oxygene equivalent to the above C# code.

The Oxygene Approach


   routes.MapRoute
   (
      'Default',
      '{controller}/{action}/{id}',
      new class
      (
         controller := 'Home',
         action     := 'Index',
         id         := ''
      )
   );

Again, the code is formatted for comparison with the previous C# example, not indicative of the style I would actually use.

We can see that Oxygene uses a very similar syntax to the C# version.

The primary difference is that we use the new class combination of keywords to indicate that we are instantiating a POCO, rather than an implementation of an interface (new interface).

Unlike C#, with its use of what to me looks like a perversely formatted pseudo-statement block, Oxygene employs a consistency in it’s approach.

The members of an anonymous class are defined and initialised with what is essentially (in Oxygene terms) an “extended constructor”.

The result is that in Oxygene we use the same syntax for initialising (and defining) the properties of an anonymous class as would be used to initialise the properties of any instantiated class in its constructor by specifying additional property assignments as “parameter expressions” following any actual constructor parameters.

You may recall, from my previous post on anonymous classes, that this is also how we provide the implementation of the methods in an anonymous class implementing an interface, by assigning method bodies to the appropriate method identifiers in similar constructor assignments.

So, What is the Point ?

As I mentioned above, anonymous classes have some advantages when compared to the alternatives such as simply encoding name/value information in a string or using some generalised class for passing around name/value pairs.

Most of the advantages should be obvious in terms of clarity or simplicity.

More than a couple of values in an encoded string quickly becomes a pain to read and maintain, not to mention the possible challenges of safely encoding values with more than just simple alphanumeric content in the values; instantiating and initialising a dictionary or other similar collection of name/value pairs is awkward and cumbersome by comparison, to say the least.

But beyond this, the main strength of anonymous classes is the type safety they provide.

That might sound like a strange thing to say about a class that by definition has no identifiable type. Even though it may not be obvious, an anonymous class is in fact strongly typed. More obviously, the member properties are.

Consider this code:


  var foo := new class( Bar := 'Forty two' );
  var s   := foo.Bar;

Q: What type is foo ?
A: object

Well, yes and no. Technically what happens is that compiler will derive a complex and unique name for the class, since we did not provide one ourselves (the same thing happens when we use an anonymous class to implement an interface).

But since we don’t know what that class name is in our code then for the purposes of passing these things around, the ultimate ancestor of all classes is all we have to work with. You could get at the generated classname if you really wanted, but it isn’t going to be of much use that I can see.

How about these…

Q: What type is foo.Bar ?
Q: What type is s ?

This is far more straightforward: Both are string.

The type of foo.Bar is inferred as string and so, being strongly typed, the type of s is itself inferred from the type of foo.Bar.

We can prove this by forcing a compilation error, explicitly declaring the type of the in-place variable s as a type not assignment compatible with a string:


  var foo := new class( Bar := 'Forty two' );
  var s: Integer := foo.Bar;  // Compiler says NO!

And this code works whether compiling for .net, Java/Android or iOS/macOS.

Visual Studio code completion will even present the Bar property for the foo object, along with all the usual members we expect on every object, so for .net:

And for Java/Android:

And Linux/Win32/Win64:

In the latter case you might be wondering where the very idea of an object comes from in projects of this type ?

The answer is that for the Island platforms (the umbrella name for the Win32/64 and Linux targets) there is a minimal RTL provided and built-in, to make core language features such as this even work (among other things).

Pretty neat.

3 thoughts on “Anonymous Classes: Anonymous POCO’s

  1. Never seen Delphi syntax so compact!

    Incidentally, “The anonymous class (or dynamic object) instantiated here in the C# will have three properties…” – doesn’t quite scan. Is there possibly a superfluous definite article in there?

  2. I think you should not call instances of anonymous classes dynamic objects because dynamic objects are something different.

    1. Hmm, yes quite right. There’s a bit of overloading going on here because in JavaScript these things are called dynamic objects, but of course in that case there is no distinction between named and anonymous types.

      I shall revise to make the distinction clearer when I get the chance. Thanks.

Comments are closed.