Var vs Dynamic vs Object and Duck Typing

C# is said to be a strongly typed language. But is it?

Maybe it depends on your point of view and definition. It is interesting that what is considered “strongly-typed” and “weakly-typed” don’t have widely-accepted definitions.

However, according to MSDN it states: “C# is a strongly-typed language”. But is C# really a strongly typed language?

C# for the most part is a strongly-typed / statically typed language. This means that the compiler knows about the type of a variable at compile time. Thus, it could be considered a type safe language since it prevents a value of one type be set as another type if they are incompatible.

So when a variable or constant is defined, it has a pre-defined type (the type of value it will store). Sounds simple enough, but let’s take a look at some “weakly-typed” / “loosely-typed” / Dynamic features of C#.

Object:

Object been around since C# 1.0. Although it seems dynamic, it’s not too dynamic. System.Object can hold a reference to any type of object and it can be assigned values of any type, however the only way to access specific methods and properties of the object, is to cast it back to its full object type. Of course .ToString() . GetType() .Equals() .GetHashCode() that are provided for all objects.

Here’s a code example that shows System.Object can accept values of any data type/object. When its cast back to its original object type, then its defined public method and property can be accessed:

using System;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            int testInt = 99;

            object testobj;

            testobj = testInt;

            Console.WriteLine("testobj : " + testobj);
            Console.WriteLine("testobj.GetType() : " + testobj.GetType());
            Console.WriteLine("testobj.ToString() : " + testobj.ToString());
            Console.WriteLine("testobj.Equals(testInt) : " + testobj.Equals(testInt) + "\n");

            object anotherTestObject = new AnotherTest();

            Console.WriteLine("object anotherTestObject = new AnotherTest(); : " + anotherTestObject + "\n");

            AnotherTest anotherTest = (AnotherTest)anotherTestObject;

            Console.WriteLine("AnotherTest anotherTest = (AnotherTest)anotherTestObject; : " + anotherTest);
            Console.WriteLine("anotherTest.Equals(anotherTestObject) : " + anotherTest.Equals(anotherTestObject));
            Console.WriteLine("anotherTest.AnotherTestInt : " + anotherTest.AnotherTestInt);
            Console.WriteLine("anotherTest.TripleValue() : " + anotherTest.TripleValue() + "\n");
        }

        internal class AnotherTest
        {
            public AnotherTest()
            {
                AnotherTestInt = 3;
            }

            public int AnotherTestInt { get; set; }

            public int TripleValue()
            {
                return this.AnotherTestInt * 3;
            }
        }
    }
}

Output results:

testobj : 99
testobj.GetType() : System.Int32
testobj.ToString() : 99
testobj.Equals(testInt) : True

object anotherTestObject = new AnotherTest(); : ConsoleApplication1.Program+AnotherTest

AnotherTest anotherTest = (AnotherTest)anotherTestObject; : ConsoleApplication1.Program+AnotherTest
anotherTest.Equals(anotherTestObject) : True
anotherTest.AnotherTestInt : 3
anotherTest.TripleValue() : 9

Sometimes System.Object is used when there’s no way to identify the object type at compile time (such as when using System.Reflection / Getting value from COM API.

Dynamic:

The Dynamic type was introduced in C# 4.0. The dynamic type tells the compiler to skip type checking at compile time and instead do the type checking at runtime. If assignments are made that are not valid then errors are thrown at runtime.

Does the Dynamic keyword make C# a weakly typed language?

If your definition of strongly typed means the compiler will do type checking at compile time and not deferring it to runtime, then dynamic makes C# weakly typed.

So then is C# really strongly typed? Maybe the best answer is … most of the time.

As of version 4, it is said the C# is actually both strongly and weakly typed language.

Regarding performance, dynamic does introduce overhead (although the CLR cache does make subsequent calls faster) vs. a strongly typed object, but it’s far faster than reflection.

Here’s a code example using Dynamic that shows that the compiler will identify the type of Dynamic at runtime. In the code sample, it will set the Dynamic first to an int, incrementing successfully without error and then set the Dynamic to a string. However, when incrementing the Dynamic that is now set as a string a runtime error will be thrown (verses a compile time error).

using System;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            object testObject = 1;
            dynamic testDynamic = 1;

            //testObject++; // Compile time error if uncommented + or ++ not allowed on object
            testDynamic++; // No compiler error - will not identify type at compile time

            Console.WriteLine("testObject.GetType() : " + testObject.GetType());
            Console.WriteLine("testObject.ToString() : " + testObject.ToString());
            Console.WriteLine("testDynamic.GetType() : " + testDynamic.GetType());
            Console.WriteLine("testDynamic.ToString() : " + testDynamic.ToString());

            // set testDynamic to a string
            testDynamic = "set testDynamic to a string";

            Console.WriteLine("testDynamic.GetType() : " + testDynamic.GetType());
            Console.WriteLine("testDynamic.ToString() : " + testDynamic.ToString());

            testObject = "set testObject to a string";
            Console.WriteLine("testObject.GetType() : " + testObject.GetType());
            Console.WriteLine("testObject.ToString() : " + testObject.ToString());

            testDynamic++; // thows a run time exception
        }
    }
}

Output Results:

testObject.GetType() : System.Int32
testObject.ToString() : 1
testDynamic.GetType() : System.Int32
testDynamic.ToString() : 2
testDynamic.GetType() : System.String
testDynamic.ToString() : set testDynamic to a string
testObject.GetType() : System.String
testObject.ToString() : set testObject to a string

Runtime Exception:

Dynamic Runtime Exception

Since Dynamic objects are not checked at compile-time, there is no IntelliSense as the compiler doesn’t know the type of the object to show its properties and methods for that object. Those are only known at runtime, when the object gets resolved.

Dynamic does allow the type of value to change after it is assigned to initially, just like System.Object allows. However, there is no type safety, so at compile time, mistakes could be made by calling any methods that do not exist or making invalid casts or assignments.

In CIL (Common Intermediate Language) (CIL was formerly MSIL (Microsoft Intermediate Language)) Dynamic’s are converted to System.Object.

The following example using the CIL disassembler ildasm.exe (this CIL Disassembler comes with Visual Studio, but there are other/better disassemblers available) shows that Dynamic becomes system.object.

object testObject = 1;
dynamic testDynamic = 1;

CIL Object vs Dynamic

One would want to add sufficient test coverage (unit and integration tests) to check that assignments are working as expected and it has the correct value.

Some uses of the Dynamic type, could be to Interoperate with other dynamic Languages such as IronPython / IronRuby. Another example of Dynamic is the MVC ViewBag that can hold anything object type. Dynamic could also be used as a return type or an input parameter.

Dynamic can be assigned null, but var cannot since, type must be established during the declaration. The compiler will not accept var x = null because it doesn’t associate the null with any type. Let’s talk about var in more detail.

Var:

The var keyword was introduced in C# 3.0.

Var is actually a strongly typed object just as if the type was specifically declared in code, but the compiler determines the type at compile time. The type is determined based on the type of value that it contains / initialized with.

However, unlike dynamic or system.object, var does not allow the type of the value assigned to be changed after it has already been assigned. So if the var was assigned an integer value, it will be treated as an integer from then onward.

Var does provide the same type safety as using a strongly typed variable as errors caught at compile time. There is also IntelliSense on var’s, since its type is known by the compiler.

If looking at the generated CIL code, it will be exactly as if the var was declared with a specific type. For example, writing int testint = 1; is the same as writing var testvar = 1; Using var is the “shorthand” (well in this case no keystroke savings would be achieved) for the type as seen below:

int testint = 1;
var testvar = 1;

CIL car vs int

In the following code example, there would be a compiler error if the var testvar that was originally set to an integer, was set to a string, since the complier already determined that the type of the var was an int32.

using System;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            int testint = 1;
            var testvar = 2;

            Console.WriteLine("testint.GetType() : " + testint.GetType());
            Console.WriteLine("testint.ToString() : " + testint.ToString());
            Console.WriteLine("testvar.GetType() : " + testvar.GetType());
            Console.WriteLine("testvar.ToString() : " + testvar.ToString());

            testvar++; // no cast required because it's already System.Int32

            // testvar = "test string"; // Compiler error: 
                                        // Cannot implicity convert type 'string' to 'int'

        }
    }
}

Output:

testint.GetType() : System.Int32
testint.ToString() : 1
testvar.GetType() : System.Int32
testvar.ToString() : 2

If you have used ReSharper, you will find that it loves to recommend/suggest many things to be a var, even an int:

Resharper Suggests var

Although you can turn this option off by setting the “Use ‘var’ keyword when initializer explicitly declares type” to “Do not show”.

Resharper suggests var

But to make an Int a Var, might not be/is not too beneficial. It really needs to be decided on case by case basis. It shouldn’t be based on laziness or lazy crutch letting the compiler do the work. One factor in deciding might be if it makes the code easier to read for future refactoring. Another reason could be if using a var could save a lot of typing, as in this example below:

MySuperLongNamedObject superObj = new MySuperLongNamedObject();

will produce the exactly same CIL as :

var superObj = new MySuperLongNamedObject();

They will be identical.

Some have suggested that is proper to declare primitive types (int, string, double, boolean, etc) using its type and more complex types (List, Dictionary<TKey, TValue>, some object) using var.

Var could cause some decrease in code readability. I am not suggesting using Hungarian Notation, but having a very descriptive var name would benefit all.

For example:

var someValue = GetSomeValueTypeNotObvious();

Naming the var to a more descriptive name is important for clarity.

Var comes in handy when creating anonymous types. The following example shows an anonymous type (var product) that was initialized with two properties names Id and Name:

using System;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            var product = new { Id = 1, Name = "Test Product" };

            Console.WriteLine("product : " + product);
            Console.WriteLine("product.Id : " + product.Id);
            Console.WriteLine("product.Name : " + product.Name);

            Console.WriteLine("product.GetType() : " + product.GetType());
        }
    }
}

Output:

product : { Id = 1, Name = Test Product }
product.Id : 1
product.Name : Test Product
product.GetType() : &lt;&gt;f__AnonymousType0`2[System.Int32,System.String]

The GetType() method of the Anonymous Type shows that the two properties were inferred correctly as Int32 and String.

Var is also very useful when using LINQ queries. They are typically used with a LINQ select clause.

using System;

namespace ConsoleApplication1
{
    using System.Collections.Generic;
    using System.Linq;

    public class Program
    {
        static void Main(string[] args)
        {
            List items = new List();

            items.Add(new Item() { Id = 1, Name = "ItemOne", Price = 1.00 });
            items.Add(new Item() { Id = 2, Name = "ItemTwo", Price = 2.00 });
            items.Add(new Item() { Id = 3, Name = "ItemThree", Price = 3.00 });

            var buckOrMoreItems = items.Where(x =&gt; x.Price &gt; 1.00);

            foreach (Item item in buckOrMoreItems)
            {
                Console.WriteLine("item.Name : " + item.Name);
            }
        }

        public class Item
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public double Price { get; set; }
        }
    }
}

Output:

item.Name : ItemTwo
item.Name : ItemThree

In the above example, var could have also been used for declaring the List and in the foreach for item.

The var buckOrMoreItems becomes IEnumerable that is used to iterate over the query in the foreach statement.

Duck Typing:

The typical duck test is the following: If it looks like a duck, swims like a duck and quacks like a duck, then it’s probably a duck.

In a duck-typed language, rather than caring about the specific object type, any object that has particular methods (Swim and Quack for example) would be accepted. If they are not present/defined it would produce a run-time error.

Some have referred to this as loosey goosey barnyard programming that goes against the norm.

In Ruby, classes are defined by what they “do”, not what they are.

C# compiler uses duck typing to determine whether an object can be used in a foreach loop. For example, it is not necessary for an object to inherit from IEnumerable or IEnumerator to be enumerated with foreach. As long as the object implements the methods GetEnumerator(), MoveNext(), Reset() and Current property it will work.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.