I'm using reflection to loop through a Type
's properties and set certain types to their default. Now, I could do a switch on the type and set the default(Type)
explicitly, but I'd rather do it in one line. Is there a programmatic equivalent of default?
13 Answers
- In case of a value type use Activator.CreateInstance and it should work fine.
- When using reference type just return null
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
In the newer version of .net such as .net standard, type.IsValueType
needs to be written as type.GetTypeInfo().IsValueType
You can use PropertyInfo.SetValue(obj, null)
. If called on a value type it will give you the default. This behavior is documented in .NET 4.0 and in .NET 4.5.
If you're using .NET 4.0 or above and you want a programmatic version that isn't a codification of rules defined outside of code, you can create an Expression
, compile and run it on-the-fly.
The following extension method will take a Type
and get the value returned from default(T)
through the Default
method on the Expression
class:
public static T GetDefaultValue<T>()
{
// We want an Func<T> which returns the default.
// Create that expression here.
Expression<Func<T>> e = Expression.Lambda<Func<T>>(
// The default value, always get what the *code* tells us.
Expression.Default(typeof(T))
);
// Compile and return the value.
return e.Compile()();
}
public static object GetDefaultValue(this Type type)
{
// Validate parameters.
if (type == null) throw new ArgumentNullException("type");
// We want an Func<object> which returns the default.
// Create that expression here.
Expression<Func<object>> e = Expression.Lambda<Func<object>>(
// Have to convert to object.
Expression.Convert(
// The default value, always get what the *code* tells us.
Expression.Default(type), typeof(object)
)
);
// Compile and return the value.
return e.Compile()();
}
You should also cache the above value based on the Type
, but be aware if you're calling this for a large number of Type
instances, and don't use it constantly, the memory consumed by the cache might outweigh the benefits.
This is optimized Flem's solution:
using System.Collections.Concurrent;
namespace System
{
public static class TypeExtension
{
//a thread-safe way to hold default instances created at run-time
private static ConcurrentDictionary<Type, object> typeDefaults =
new ConcurrentDictionary<Type, object>();
public static object GetDefaultValue(this Type type)
{
return type.IsValueType
? typeDefaults.GetOrAdd(type, Activator.CreateInstance)
: null;
}
}
}
The chosen answer is a good answer, but be careful with the object returned.
string test = null;
string test2 = "";
if (test is string)
Console.WriteLine("This will never be hit.");
if (test2 is string)
Console.WriteLine("Always hit.");
Extrapolating...
string test = GetDefault(typeof(string));
if (test is string)
Console.WriteLine("This will never be hit.");
I do the same task like this.
//in MessageHeader
private void SetValuesDefault()
{
MessageHeader header = this;
Framework.ObjectPropertyHelper.SetPropertiesToDefault<MessageHeader>(this);
}
//in ObjectPropertyHelper
public static void SetPropertiesToDefault<T>(T obj)
{
Type objectType = typeof(T);
System.Reflection.PropertyInfo [] props = objectType.GetProperties();
foreach (System.Reflection.PropertyInfo property in props)
{
if (property.CanWrite)
{
string propertyName = property.Name;
Type propertyType = property.PropertyType;
object value = TypeHelper.DefaultForType(propertyType);
property.SetValue(obj, value, null);
}
}
}
//in TypeHelper
public static object DefaultForType(Type targetType)
{
return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
}
The Expressions can help here:
private static Dictionary<Type, Delegate> lambdasMap = new Dictionary<Type, Delegate>();
private object GetTypedNull(Type type)
{
Delegate func;
if (!lambdasMap.TryGetValue(type, out func))
{
var body = Expression.Default(type);
var lambda = Expression.Lambda(body);
func = lambda.Compile();
lambdasMap[type] = func;
}
return func.DynamicInvoke();
}
I did not test this snippet, but i think it should produce "typed" nulls for reference types..
Slight adjustments to @Rob Fonseca-Ensor's solution: The following extension method also works on .Net Standard since I use GetRuntimeMethod instead of GetMethod.
public static class TypeExtensions
{
public static object GetDefault(this Type t)
{
var defaultValue = typeof(TypeExtensions)
.GetRuntimeMethod(nameof(GetDefaultGeneric), new Type[] { })
.MakeGenericMethod(t).Invoke(null, null);
return defaultValue;
}
public static T GetDefaultGeneric<T>()
{
return default(T);
}
}
...and the according unit test for those who care about quality:
[Fact]
public void GetDefaultTest()
{
// Arrange
var type = typeof(DateTime);
// Act
var defaultValue = type.GetDefault();
// Assert
defaultValue.Should().Be(default(DateTime));
}