Non-generic default values in C#? Why not?!
A relatively obscure new keyword in C# 2.0 is the default(T) keyword.
It's shorthand for checking if the given generic parameter T is a value type or reference type. If it's a reference type, it returns null. Otherwise it returns 0/false/0.0 or whatever the default value for the type is.
For some reason, though, it can only be used on generic parameters. Why can't I use default(typeof(int)) to get 0? Or, more realistically, default(myUnknownType) to return the default value?
A workaround is to check if it's a value type with reflection, but it's cumbersome:
if (!myType.IsValueType)
{
return null;
}
else
{
return Activator.CreateInstance(myType);
}
Luckily, value types (including structs) always have a default parameterless constructor. We can't even override it - it will always initialize all struct members to their default values.
This little code snippet can now be wrapper in some sort of function - call it Default() or GetDefault() or even @default() if you want to keep it as close as possible to the generic keyword.
It's interesting to note that the generic keyword relies on the InitObj IL opcode to initialize the object. If we could embed IL directly in our code we could call this directly. We can do it via Reflection.Emit and DynamicMethods but I feel that's going a bit far overboard when the solution above works just as well.
32 Comments
Comments have been disabled for this content.
Gabriel Lozano-Morán said
Instead of
int i = default(typeof(int));
Why don't you use?
int i = new int();
Wesner Moise said
Use default(int) instead of default(typeof(int))
default(T) takes a type name, not a type value.
However, this feature is not useful outside generics... because for value types you can use new T() and for reference types, null...
Avner Kashtan said
Yes, as you can see from the rest of my post this is exactly what I do - I instantiate the value type with the default constructor.
I'm just saying it would be nicer to have the shorter syntax - default(myType), for instance.
Wesner Moise said
You missed my point.... The feature you requested already exists... You typed it wrong.. default(myType) works everywhere. but default(typeof(myType)) works nowhere.
AvnerK said
Werner - that's exactly my point. I can do default(int) right now, or default(float) or even default(myStruct), but I can't do a default(myRuntimeType). If I received a Type object and want to return that type's default value I have to use the function I outlined above, rather than using a clearer, more concise language construct such as default(type).
Gabriel Lozano-Morán said
Dude, I believe that you are totally abusing the default keyword. You should only use it in Generics to assign a default value to your parameterized type. Using it outside generics you should call the constructor of the type as I posted earlier int i = new int(); float f = new float(); MyObject mo = new MyObject();
Gabriel said
I think I finally understand what you mean and the answer is no you can't and here is why: you are trying to dynamically create types (thus during runtime) based on another runtime type. You are mixing compile-time with run-time here.
Avner Kashtan said
Mixing compile-time and run-time is what Reflection is all about. As my sample above shows, I can achieve my goal using Activator.CreateInstance today - I instantiate a new value type at runtime using a type that's unspecified at compile time. What I want is a better way to do what that snippet does - initialize a variable without having to go through its constructor and the Activator methods and everything.
Gabriel said
It has to do with the type-safety checks in the CLR why this is not allowed. When you use Generics this will be JIT-ted which you still cannot compare with Reflection.
Jacob said
Thanks! Needed that little snippet; here's my usage to get some data over ADO.NET: // Generic version public static T ReadValue(object value) { return value == DBNull.Value ? default(T) : (T) value; } // Runtime Type version public static object ReadValue(object value, Type targetType) { object defaultValue = targetType.IsValueType ? Activator.CreateInstance(targetType) : null; return ReadValue(value) ?? defaultValue; }
Rene Dohan said
Thanxs for snippet
espinete said
Resume, mister, which the best solution about this issue ? Greetings
Amber said
Thank Man, That is cool trick for default initialization of object in generic.
Jason said
The is the best solution I have seen to create default value of dynamic type. Awesome!
Miron said
Very nice solution, Thanks for sharing.
Darius Damalakas said
I just happened to need this trick to do myself. Thanks for idea. I wonder, how Activator is internally creating your given value type, and what is the performance cost of that
mehfuzh said
Nice solution. This is also important. when you are doing to IL parsing after return from a method that should contain default value.
Charles T. said
I solved this issue with an extension method. I put it in a try catch just in case people also try to call the extension from a non-initialized object. public static class Extensions { /// /// Determines if a given object is set to its default value or not /// ///
/// /// public static Boolean IsDefaultValue(this T val) { try { return ((typeof(T).IsValueType) ? (default(T).Equals(val)) : (null == val)); } catch (NullReferenceException nex) { return true; } } }
Job Vermeulen said
Your extention doens't work always. Example: int i = 0; object objectI = i; objectI.IsDefaultValue() returns false Because the value is compared at the current type (object in this case), not the highest type (int32). I changed it to this (it can be shorted but less readable) public static Boolean IsDefaultValue(this T val) { try { if (val.GetType().IsValueType) { return (GetDefaultValue(val.GetType()).Equals(val)); } return false; } catch (NullReferenceException nex) { return true; } } public static object GetDefaultValue(Type type) { if (!type.IsValueType) return null; object defaultValue; lock (DefaultValueTypes) { if (!DefaultValueTypes.TryGetValue(type, out defaultValue)) { defaultValue = Activator.CreateInstance(type); DefaultValueTypes[type] = defaultValue; } } return defaultValue; }
Jo said
The reason is that default(T) where T is a compile-time type is well understood and T is either defined at compile time or not(which will prevent execution if it is not). but default(SomeType) is not well defined at compile time since SomeType, being a type, could be null or anything else. What is default(null)? Remember, default(T) is suppose to always return a correct default at compile time. if we have int i = default(someType) then what if someType is null? doesn't work does it? you cannot do int i = default(float); because the compiler knows the return type of default at compile time and issues an error. but when you start doing that kinda stuff at run-time it is no longer a true "default" value and can cause some major bugs. even things like int i = default(typeof(int)) are problematic for the exact same reasons. How would you solve the problem above if default worked on types? int i = default(typeof(int)); // logical by then why not just default(int)??? int i = default(typeof(someClass)); // no compile time error but surely a runtime error? So the real issue is that default(Type) is different than default(T) where T is a generic parameter. default(T) is meant to return a well-defined default value that always works regardless. If you allow default to work on types, e.g., default(type), then you end allow allowing potential bugs in your code. If you really want default values then just create an interface and have all your types that need it to implement it. say interface IDefault { T Default(); } if you want to also extend Default to built-in types then use extensions.
Guilherme Coelho said
In this case, what can i do?! i need default(typeof(myObj.GetType())) works. i Have to much properties and field to clear all one by one. public void Dispose() { foreach (FieldInfo field in this.GetType().GetFields(BindingFlags.NonPublic)) { this.GetType().GetField(field.Name).SetValue(this,default((Type)this.GetType().GetField(field.Name).GetType())); } }
Dirty Harry said
Avner is absolutely correct here to complain about this. What if you have code that must copy a db field of unknown type to type to some other field whose type is given to you at runtime? We can check the db field and if it isn't DBNull we copy it, but if it IS DBNull we cannot, so instead we want to copy the default value for the target type. But if the target type's type is only available at runtime, we cannot use the default() keyword can we?
ipad app programmersDA said
Politeness costs nothing and gains everything. -----------------------------------
ipad reviews said
----------------------------------------------------------- My English communication is not so super butI believe I understand everything. Thank u so much for that fantastic blog publish. I genuinely enjoy studying it. I believe you are a absolute author. At this second additional ur website to my favorites and will appear once more to yor internet web page. Maintain up that wonder perform. Hope to find out more quickly.
cordless phones reviewsDA said
I prefer to require breaks in the course of the my day and browse by way of some blogs to determine what men and women are talking about. This weblog appeared in my searches and that i could not assist clicking on it. I'm pleased that I did because it was a very intriguing read. -------------------------------------------------------------------- Folklore and Mythology
weblogs.asp.net said
Non_2D00_generic default values in C_23003F00_ Why not_3F002100_.. Super :)
Vivienne MikhailDA said
Hi there, You have completed an extraordinary job. I will undoubtedly digg it and personally recommend to my buddies. I am sure they are going to be benefited from this web-site.
Marcos Lima said
Hey!.... What do you think? public static bool IsEmpty(this object value) { dynamic newInstance = Activator.CreateInstance(value.GetType()); if (value == null) return true; if (value is ValueType) { return value.Equals(newInstance); } else { return value.Equals(newInstance); } }
Marcos Lima said
Pls... just ignore the "if (value is ValueType)"...
Marcos Lima said
And now... What do you think... public static bool IsEmpty(this object value) { dynamic newInstance = Activator.CreateInstance(value.GetType()); if (value == null) return true; if (value is IComparable) { //If the value is an IComparable, and the compare result is 0, then is a default value return (newInstance.CompareTo(value) == 0 ? true : false ); } else { return value.Equals(newInstance); } }
Mark Jones said
Your question pertains primarily to Types known at compile time. The issue becomes more complex if you want to know the default value of an arbitrary Type at run time. Try the following methods, which I have written and tested against thousands of types: /// /// [ public static T GetDefault< T >() ] ///
/// Retrieves the default value for a given Type /// /// The Type for which to get the default value /// The default value for Type T /// /// If a reference Type or a System.Void Type is supplied, this method always returns null. If a value type /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an /// exception. /// /// public static T GetDefault() { return (T) GetDefault(typeof(T)); } /// /// [ public static object GetDefault(Type type) ] ///
/// Retrieves the default value for a given Type /// /// The Type for which to get the default value
/// The default value for /// /// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null. If a value type /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an /// exception. /// /// public static object GetDefault(Type type) { // If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null if (type == null || !type.IsValueType || type == typeof(void)) return null; // If the supplied Type has generic parameters, its default value cannot be determined if (type.ContainsGenericParameters) throw new ArgumentException( "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type contains generic parameters, so the default value cannot be retrieved"); // If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct), return a // default instance of the value type if (type.IsPrimitive || !type.IsNotPublic) { try { return Activator.CreateInstance(type); } catch (Exception e) { throw new ArgumentException( "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe Activator.CreateInstance method could not " + "create a default instance of the supplied value type (Inner Exception message: \"" + e.Message + "\")", e); } } // Fail with exception throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type is not a publicly-visible type, so the default value cannot be retrieved"); } The first (generic) version of GetDefault is of course redundant for C#, since you may just use the default(T) keyword. Enjoy!
Timwi said
Hi. This blog entry makes you appear heavily misinformed, although to be fair the comments seem to indicate that you do know your stuff, you just haven’t really worded it very well. Here are some corrections: • default(T) is *not* for checking if a type is a value type or reference type. It is simply a way of returning a default value (one which, in technical terms, corresponds to binary zeroes). • It is not true that default(T) can only be used on generic parameters. You can write default(int) just fine. You refer to it as a “generic keyword” but it has nothing as such to do with generics (any more than typeof() does, anyway). • It is true that you can’t use default() on run-time types (variables of type “Type”). It should be quite clear why you can’t do that: the “Type” type is just an API, not really a feature of the C# language. The correct solution would be to have a method in the Reflection API, perhaps Type.GetDefaultValue(), which would do the equivalent of your snippet. I do wonder why such a method doesn’t exist. • You correctly discovered that default(T) translates into the IL instruction “initobj”. However, the “initobj” opcode expects a type metadata token as an operand, so you can’t use it on a variable of type “Type”, so it can’t do any more than default(T) can. So being able to embed IL directly into your code wouldn’t help.