C# Implicit Types: Dynamic, Var and Object

TRAPDOOR Labs
4 min readJun 21, 2020

--

C# provides several different ways to represent variable types. This article explores the differences between var, dynamic and object and takes a deeper look at what’s happening behind the scenes.

Photo by Amy Shamblen on Unsplash

System.Object

The System.Object (aka object) is the base Type for all other types in C#. An object can store any value and any other Type can be implicitly converted to it:

object type_object_1 = “hello”;
object type_object_2 = 3; // Value type
object type_object_3 = true; // Value type
object type_object_4 = new MyCustomClass();

Implicit conversions: No special syntax is required because the conversion is type safe and no data will be lost. [Microsoft Docs]

An object is by far the best way to declare variables without a type though. To use an object you need to cast it back to the required type each time and in some cases there may be a performance impact.

🥊 Boxing and Unboxing

Whenever a value type is converted to an object a new one is created on the heap and data copied in. This is known as boxing.

To unbox an object an explicit conversion is required and the compiler will throw an error if not provided. Without the (int) cast below, the compiler will throw an error (Error CS0266 Cannot implicitly convert type ‘object’ to ‘int’).

// Unbox to an int
object type_object = 3;
int unboxed_int = (int)type_object;

The unboxing process adds an overhead behind the scenes as it has to validate the type and then copy the data back. As this happens at runtime the compiler can’t check a cast is valid and instead an exception will be thrown.

// Invalid unbox will throw System.InvalidCastException
object type_object = 3;
string unboxed_int_fail = (string)type_object;

Var (Implicitly Typed Variables)

The ‘var’ keyword is used to declare an implicitly typed variable. Using vars are type safe as the compiler will determine the type based on what you initialise the variable to.

So this:

var type_implicit = “hello”;

Is exactly the same when compiled as:

string type_explicit = “hello”;

Below is how these variables are initialised in the IL (Intermediate Language) as output from the compiler.

.locals init (
[0] string type_implicit,
[1] string type_explicit,

There are several restrictions when using ‘var’ such as:

  • Only for variables defined in a method body
  • Not used as a return type
  • Cannot be set to null (i.e. var test = null;is not valid)
  • Must be initialised (i.e. var test; is not valid either)

As with an explicitly declared variable, a ‘var’ cannot be re-assigned to another type (i.e. no implicit type conversion):

var type_implicit = “hello”;
type_implicit = 5; // Error CS0029 Cannot implicitly convert type 'int' to 'string' DynamicTypes

When to use implicit vs explicit really comes down to personal coding standards and consistency within the project. There is no runtime performance hit or change in program logic as both compile to the same IL. Preference will generally come down to readability. At a minimum we’d suggest using them inside ‘foreach’ loops and ‘using’ statements.

Anonymous Types

Note: The only time using ‘var’ is enforced is when dealing with anonymous types.

Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first. [Microsoft Docs]

var type_anonymous = new { Message = “hello”, MessageTime = DateTime.Now };Console.WriteLine($"{type_anonymous.Message} at {type_anonymous.MessageTime}");

Dynamic

Contrary to a ‘var’, an element typed as ‘dynamic’ will have its type resolved at runtime. This means no compile time checking, added risk to your codebase and a large performance hit. They should only be used in situations when the type isn’t known at compile time, such as when working with COM.

Once compiled, a ‘dynamic’ is usually represented as a System.Object (but without the compile time checks!). So these two variables:

dynamic type_dynamic = “hello”;
object type_object = “hello”;

Will both be initialised in IL as objects…

.locals init (

[2] object type_dynamic,
[3] object type_object,

… but behave very differently. A simple function call using ‘type_dynamic’ as a parameter will result in a hefty amount of hidden IL code to perform the required runtime reflection and checks.

From a developer perspective the problem with no compile time checks means you can simply pass around and return dynamic variables without any warnings or errors. Take this simplified example where we set our variable to an integer and then call a function that expects a string.

    ... 
dynamic type_dynamic = 5; // or call SomeExternalFunction();
// This will now throw an exception at runtime
PrintString(type_dynamic);
}
static void PrintString(string message)
{ … }

This code will happily compile but when it finally gets run in production a RuntimeBinderException will be thrown.

Exception to the Rule

In most documentation that covers System. Object it will state something like “System.Object is the base Type for all other types in C#”. This is true for 99% of cases…

static void Main(string[] args)
{
fixed(int* pointer_to_int = &a[0])
{
// Compiler Error CS0029 Cannot implicitly convert type 'int*' to 'object'
object object_pointer = pointer_to_int;
}
}

Pointers are the exception to the rule but are fairly uncommon in C#. They also require you to compile with the unsafe flag. If required, pointers can be converted to an IntPtr which can be used as a value type.

--

--