JS Programming in C# — Objects on the fly

Creating objects without defining types is considered a great flexibility in JavaScript. JavaScript treats objects nothing more than key-value pairs. Yeah, you can declare functions in the object but they are still part of the key-value philosophy. Bottomline: JavaScript did not start out as an object oriented language.

Typically, objects are created on the fly[1] using the object literal syntax:

let obj = {  
name: "Will",
age: 26
};

There are scenarios when creating objects on the fly is indeed a flexibility.

One example of such a scenario is when the input to a service/API does not have a fixed definition. Consider a web service API that takes a bunch of field names and corresponding values to be updated. Let us say that the API responds with a status code and message denoting the effect of the update. The set of fields to be updated, although restricted to a predefined set, is not fixed on a specific subset or the entire set for any given update. It is left to the caller and/or scenario. Some highly flexible update API!

If you are a developer from the static typing world (C#, Java or the like), I bet you would go to great lengths to represent the above loosely defined type using conventional static typing thought process. Don’t feel discouraged if you eventually end up with a glorified wrapper over a Map/Dictionary, which doesn't really provide any type benefits (from our API's point of view). Any definition of Map<K, V> will not be able to guarantee if a given key is valid let alone the type of its values.

Also, when you are creating an object on-the-fly, it is not restricted to top-level elements. It is possible to nest objects (as in address below).

let fields = {  
address: {
houseNo: "920",
aptNo: "14B"
},
vehicle_regn: "AB123D3Y"
};

In the snippet above, we intend to update the vehicle registration and address, particularly the house and apartment number. Perhaps, the person bought a new car and moved to a different apartment community on the same street!

In general, any scenario where the set of (input) data elements is variable/dynamic and particularly passed off for consumption elsewhere begs the need for object creation on the fly.

The party interested in the object created is not the creator itself but a third-party.

The corollary is that the consumer (third-party) receiving this object would require a static type definition or something of the sort. However, the entity that creates object on-the-fly is expected to create the values with valid rather than arbitrary types for desired results.

Alright, let us try another example. Consider the above web service responding with a status code and message for an update. You could define a type for the response:

class Response<T> {  
int status;
String message;
T payload;
}

As such, the response is primarily intended to be consumed by the caller and not the service itself. Since the client may not be written in the same language as the service, the above type definition may not be useful. If the service was implemented JavaScript/NodeJS, it is natural to create the response object using object literals.

Would we find such a thing in C#? I mean creating objects on the fly.

C# provides anonymous types in par with the JavaScript’s object literal.

var response = new {  
Status = 0,
Message = "Operation completed successfully"
};

Unlike JavaScript’s object literal, anonymous types are statically typed. The compiler takes care of declaring the type for us. Although anonymous types allows us to create objects on the fly, there are at least two limitations that I think we cannot overcome:

  • Anonymous types cannot[2] be return values, which makes them almost useless in our situation. I say almost because they can be still be used if we are able to construct the anonymous type right in the place of dispatch (see line #15 return Ok(anonymous type)).
public IActionResult SomeServiceMethod(int id)  
{
try
{
if (id <= 0)
{
return NotFound(); // Returns a NotFoundResult
}
        // DoTheWork cannot return anonymous type object!
// No, don't think of returning as object. We
// are talking about strong types here.
var payload = service.DoTheWork(id);
        // The parameter to Ok() method, the anonymous type,
// cannot be returned from another method, for eg.
// service.DoTheWork(..)
return Ok(new {
Status = 0,
Message = "Success!",
Payload = payload
});
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
  • The other limitation has to deal with the implementation or intrinsics of the anonymous types. Yes, the compiler is smart to detect the similarity in structure of two or more anonymous type instances and declare/use a single type (naming it however it wants). Since only the compiler knows the declared type, it cannot be pronounced anywhere in the user code. Unlike user declared types and its references, it is not possible to logically prove[3] that two instances of anonymous instances with similar structures indeed use the same type.

Even if we ignore the above issue, there is one more. In the case above where there are multiple return points, which are expected to return different instances of the same type, if you add another member to one of the return points, that return point has a different type compared to other return points. A Response with or without payload data converge to the same type but not so if it was constructed as anonymous types.

Recap

Alright, before you start pulling your hair or your head explodes, let me decode the rambling odyssey.

We were looking for a facility in C# that is similar to object literals in JavaScript. We laid our eyes on anonymous types, and were investigating its applications and limitations to see if it can match object literals; although object literals do not fall into the static typing business owing to the loose/no/dynamic typing in JavaScript.

Anonymous types in C# are limited in two ways:

  • Cannot be return values
  • Statically typed but might not converge to the same type

Although we started off with the request object, we are drawn more towards the response; maybe because it seems it is leading us somewhere. Also, the topic is not restricted to web service APIs. It applies to regular function/method calls too.

Retrospect

TypeScript covers JavaScript by allowing you to define interfaces and marking them on return types to state our expectation. TypeScript ensures that the object returned abides the interface definition. So, even if we use object literals - an unintelligent bunch of key-value pairs, it can still be type checked against the interface.

interface IResponse<T> {  
status: number;
message?: string;
payload?: T;
}
function processRequest(/* some args */) : IResponse {
    // do whatever
    {
// somewhere; another return point.
return {
status: 500,
message: "Failed"
// no payload
};
}
    // ....
    return {
status: 200,
message: "Success",
payload: { ... }
};
}

Marking the return type for processRequest ensures that the return value is not something arbitrary. Also, each return point can return IResponse like objects constructed differently; object literals, new Response(…) etc.

C# does not have such a facility. We can define interfaces but ...

  1. Anonymous types cannot be inherit an interface. Or there is no way to instruct the compiler to enforce generating the anonymous type against an interface.
  2. It is not possible to restrict an interface definition only to properties and restrict methods since we are looking for an equivalence of an JavaScript object literal.

The Verdict (Yet to be Delivered)

Let us worry about #2 above in a minute but it appears that we were looking for #1 above all along.

Annotating anonymous types with an interface makes them returnable from methods. Tada!

If we are able to instruct the compiler as follows:

interface IResponse<T>  
{
int Status {get; }
string Message { get; }
T Payload { get; }
}
public IResponse<Product> SomeMethod(/* some args */)  
{
var product = obtainProductFromSomwhere();
    // blah blah blah and then finally
return new : IResponse<Product> // Not valid C# syntax. Adding to wish list!
{
Status = 200,
Message = "OK",
Payload = product
}
}

See the new : IResponse<Product>() { … }, the annotation of an interface on an anonymous type. We should able to annotate with more than one interface.

To accommodate #2, the compiler could restrict the use of properties-only interface when marking anonymous types with an interface. For instance, the compiler could complain in the following case:

interface IResponse<T>  
{
int Status {get; }
string Message { get; }
T Payload();
}
public IResponse<Product> SomeMethod(/* some args */)  
{
// blah blah blah and then finally
return new : IResponse<Product>
{
Status = 200,
Message = "OK",
// Compiler should red-squiggle here
// because Payload is defined as a method.
Payload = ???
}
}

The compiler could restrict applying arbitrary interfaces on anonymous types and only allow ones that have only properties since we can only set value on properties, not on methods. No, no, no, we are not encouraging methods in interfaces for anonymous types by considering single-body expressions.

If the compiler were to support this new thing — deriving anonymous types from interfaces, aren’t they no more anonymous types?

  • Yes. They are no more anonymous types because you are able to pronounce their type name (by the way of an interface it implements).
  • No. Because annotating with an interface is not mandatory on any and all anonymous types. There will still be scenarios that will continue exist and work outside of this feature where anonymous types do not implement an interface such as in LINQ.

Why not just declare a class Response that implements IResponse<T>? In that case, do we even need IResponse<T>? We want to take anonymous type to our advantage, and at the same time, ensure type safety. We do not want class declarations just for the sake of it. We are not talking about Java here where the populace loves writing getters and setters; doesn't matter if they leverage an IDE vomit.

A type definition is a powerful thing, a semantic; semantic of the entity you are dealing with rather than a bunch of associated properties. Consider this, if I give you a string, can you tell me if it is a zip code or an email address? Well, you might if you are reading it as text on your screen. The compiler might not be able to unless you specifically instruct by laying down the rules of the type. Neither could you if the domain is vast (aka large code base).

When you declare a type — class, with the rules that govern if a string is a zip code or an email address, and with methods that allow you to inquire many things on the underlying data, then you have precisely defined what it means to be a zip code or an email address. In other words, two sets of identical properties need not necessarily identify the same entity such as in the case of the anonymous types returning response.

We want all the good in the world stemming from static typing, retain flexibility and also be able to express in as less words as possible the might of our intent. Less words to convey more. Hmm! Sounds lot like love 😍.

finally 😂

Although Java’s anonymous classes are along the lines of what we are proposing for C#, they lack the finnesse; restricting methods, lack of properties etc.


This post was originally published on A Developer’s Experience

  1. Objects can also be created using the function declaration syntax. The syntax and ugly mechanics is not important.
  2. Let us ignore returning as undefined and other gimmicks.
  3. You can open the assembly and confirm to your satisfaction. Assembly is a runtime artifact. But we are talking about code, compile time artifact. Besides, compiler employing type-sharing in the case of anonymous types is an optimization. Consider this: If you declare two types A and B with exactly same structure. Does the compiler attempt to optimize such as replace A and B with a single definition?