.NET

C# Dynamic Types and Anonymous Types

C# allows the programmer to use dynamic types (which are resolved at runtime) and anonymous types without needing to pre-define them. These two features can make working with C# objects almost feel like writing JavaScript (since in JavaScript objects are ultimately dictionaries and can be created in-line and passed to functions without the type-checking that C# does). While generics could be used to accomplish this same thing, it was something interesting I saw while working on a project.

Background

This project had a third party API it was calling for updating an array of objects all at once. The request body contained a field for the array of objects to update (and updated them according to the values filled for each object) and a field for the type of object to update. For example, if it was a list of drivers, the type field contained the string “Driver.” If it was a list of cars, the type field contained the string “Car.” The third party API did not allow different types of objects to be updated at once — all objects passed in had to be the same type.

To call the API, a class was created that contained methods for calling the API. Specifically, there was a single method for updating a list of objects which took a request object and constructed the API call from that request object. To ensure that the this method was able to work for any type, a dynamic type was used for creating the request object. When a list of cars or drivers needed to be updated, the request was constructed using an anonymous type and filled in either the details for cars or drivers (depending on what needed to be updated), which then was passed into the method for calling the API to update the list of objects. (All of this can be seen in the code below).

Request Object

This object is what is sent to the API client we had written for interacting with the third party API.

    public class BulkUpdateRequest
    {
        // We use the dynamic type here since this will be determined at runtime
        public List<dynamic> Objects { get; set; }
        public string Type { get; set; }
    }

Building the request and making the call

Here you can see the two different types to call the same service for the API. The idea here is that rather than duplicating code for each specific type that needed to make this call, we were able to avoid code duplication and use one method for both types. (This also can be accomplished with generics).

using System.Collections.Generic;
using System.Linq;

namespace CodeLab
{
    public class Service
    {
        private const string UpdateUrlResource = "/bulkUpdate";
        private readonly ApiClient _client;

        public Service()
        {
            _client = new ApiClient();
        }

        public IEnumerable<UpdateResponse> UpdateCars(List<Car> carsToUpdate)
        {
            IEnumerable<UpdateResponse> response = UpdateItemsAsync(new BulkUpdateRequest
            {
                // To utilize the dynamic typing for each specific type that will be passed
                //  in the request, we need to cast to type IEnumerable<dynamic>
                Objects = ((IEnumerable<dynamic>)carsToUpdate.Select(
                        x => new Car
                        {
                            Year = x.Year,
                            Make = x.Make,
                            Model = x.Model
                        }
                    )).ToList(),
                Type = "Car"
            });

            return response;
        }

        public IEnumerable<UpdateResponse> UpdateDrivers(List<Driver> driversToUpdate)
        {
            IEnumerable<UpdateResponse> response = UpdateItemsAsync(new BulkUpdateRequest
            {
                // Same case here -- we're casting our specific Driver type to dynamic
                //  which will ultimately be determined at runtime.
                Objects = ((IEnumerable<dynamic>)driversToUpdate.Select(
                        x => new Driver
                        {
                            FirstName = x.FirstName,
                            LastName = x.LastName,
                        }
                    )).ToList(),
                Type = "Car"
            });

            return response;
        }

        private IEnumerable<UpdateResponse> UpdateItemsAsync(BulkUpdateRequest request)
        {
            var response = _client.HttpPostAsync(UpdateUrlResource, request);
            return response.Data;
        }
    }
}