One of the new features of .Net 4.0 is the concept of a dynamic type. In general terms, it allows the programmer to perform “late binding” at run time. So, what’s so special about that – the object type allows “late binding”. Well, does it? What if you don’t know what type the object is but you do know it will support a method or operation? Yes, you can always use “typeof”, “is”, or “instanceof” operators to test that it is a specific type or inherits from a specific type – but what if the method or operator doesn’t have a common inheritance tree – such as most of the operators (+, –, etc) or what if there are many possibilities/combinations. The dynamic type solves a lot of the type checking and casting that would be required in previous versions of .Net.
For example, let’s say you’re wanting to do addition on two objects. Let’s assume that both these objects support +. The general concept of late binding says that you “should” be able to do something like:
1: object value1=1;2: object value2=2;3: object value3=value1+value2;
The above code will give you “Error 1 Operator '+' cannot be applied to operands of type 'object' and 'object'”. The solution is to cast these as ints such as:
1: object value1 = 1;2: object value2 = 2;3: object value3 = (int)value1 + (int)value2;
Ok, so that works – no compile error…but, what if value1 was a double, not an int…or, what if value1 or value2 could be either int or double…then you’re stuck with some nasty code like:
1: object value1 = 1;2: object value2 = 2;3: object value3;4: if (value1 is int)5: {6: if (value2 is int)7: {8: value3 = (int)value1 + (int)value2;9: }10: else if (value2 is double)11: {12: value3 = (int)value1 + (double)value2;13: }14: }15: else if (value1 is double)16: {17: if (value2 is int)18: {19: value3 = (double)value1 + (int)value2;20: }21: else if (value2 is double)22: {23: value3 = (double)value1 + (int)value2;24: }25: }26:
Add another type that both could be and it increases the code more…not nice at all.
Around .Net 3.5, Microsoft added the var type. The var type infers a type at design time. This is nice in our example case and actually works so:
1: var value1 = 1;2: var value2 = 2;3: var value3 = value1 + value2;
Actually works – it compiles and we get 3. If we change a or b to be a double, value3 gets a double result (double + int=double) as expected if we had explicitly typed the variables. Great, problem solved – right? Wrong… Let’s add another monkey wrench into this…
Suppose we want a function that converts a string to the “best” type that that string represents. So, a string value of “2.4” would return a double 2.4, a “12/25/2011” would return a DateTime of 12/25/2011, etc. Then we’d like to take the result of that and add it to another result from that same function. This would work with object, but we’d be back in the exact same situation as before – testing with “is” and casting. What about var? (the GetBestType function is a prototype only – it works but it needs more error checking, validation, etc).
Well, first thing with that code – won’t even compile. Seems the var keyword is only for local variables. Well, back to old reliable object, right? If we were using .Net 3.5 or before, yes…this is where dynamic comes in for .Net 4.0.1: var value1 = GetBestType("1.2");2: var value2 = GetBestType("2");3: var value3 = value1 + value2;4:5: public var GetBestType(string value)6: {7: double doubleResult;8: if (double.TryParse(value, out doubleResult))9: {10: return doubleResult;11: }12:13: int intResult;14: if (int.TryParse(value, out intResult))15: {16: return intResult;17: }18:19: DateTime dateResult;20: if (DateTime.TryParse(value, out dateResult))21: {22: return dateResult;23: }24:25: return value;26: }27:
The dynamic type is determined at run time…so, fixing our code to use dynamic, we can do this:
1: var value1 = GetBestType("1.2");2: var value2 = GetBestType("2");3: var value3 = value1 + value2;4:5: public dynamic GetBestType(string value)6: {7: double doubleResult;8: if (double.TryParse(value, out doubleResult))9: {10: return doubleResult;11: }12:13: int intResult;14: if (int.TryParse(value, out intResult))15: {16: return intResult;17: }18:19: DateTime dateResult;20: if (DateTime.TryParse(value, out dateResult))21: {22: return dateResult;23: }24:25: return value;26: }
Yes, we’re returning either a double, an int, a DateTime, or a string from one function and the code works as expected.
While the above code works, and minimizes code vs late binding type checking, both the dynamic and var keywords should be used sparingly in favor of code readability. Technically, while you could create every variable as var or dynamic, it would greatly reduce readability…so, use sparingly when needed (i.e. when you’d be tempted to use object and type checking). It is much better than using object, but it should never replace a explicitly typed variable, if you have the choice.
No comments:
Post a Comment