I have a little utility method that looks like so:
/// <summary>
/// Replaces a DBNull value with the default of the type specified
/// </summary>
/// <typeparam name="T">Resulting type</typeparam>
/// <param name="p_this">Object to check for DBNull</param>
/// <returns>p_this if it is not DBNull.Value, else default(T)</returns>
public static T ReplaceDBNullWithDefault<T>(this object p_this)
{
return p_this == System.DBNull.Value ? default(T) : (T)p_this;
}
In my specific scenario, I am taking a record out of a data table and taking a specific field out of it using weak types, and the specific field I'm getting is a long which is being boxed into an object. An example that reproduces this is as follows:
var obj = 2934L;
int num = obj.ReplaceDBNullWithDefault<int>();
It fails with an InvalidCastException, at (T)p_this.
I understand why, the boxed long cannot be cast directly to int, and trying to do it like this also fails:
object myLong = 234L;
int myInt = (int)myLong;
However, unboxing and then casting works fine:
object myLong = 234L;
int myInt = (int)(long)myLong;
How can I work around this in my method?
You can try this:
public static T ReplaceDBNullWithDefault<T>(this object p_this) where T : struct
{
return
p_this == System.DBNull.Value
? default(T)
: (T)Convert.ChangeType(p_this, typeof(T));
}
Nevertheless you will get an exception if you try to apply this function to a not-convertible type.
So probably it really would be better to cast the value manually to the known type.
You can use this approach:
/// <summary>
/// Replaces a DBNull value with the default of the type specified
/// </summary>
/// <typeparam name="T">Resulting type</typeparam>
/// <param name="value">Object to check for DBNull</param>
/// <param name="tryConvert">if true the object will be converted to the target type if possible, otherwise an InvalidCastException is raised</param>
/// <returns>p_this if it is not DBNull.Value, else default(T)</returns>
/// <exception cref="InvalidCastException">Thrown if the target type is incorrect and the value could not be converted to it</exception>
public static T ReplaceDbNullWithDefault<T>(this object value, bool tryConvert = true)
{
if (value == System.DBNull.Value || value == null)
return default(T);
if (value is T)
return (T) value;
if(!tryConvert || !(value is IConvertible))
throw new InvalidCastException($"Cannot convert {value.GetType()} to {typeof(T)}.");
return (T)((IConvertible) value).ToType(typeof(T), null);
}
Related
I'm trying to create a nested Guard method (or find an existing Guard library) that would throw an exception if any object in a nested hierarchy is NULL and include the name of the object hierarchy path up to and including NULL. I also want to avoid fluent API syntax of checking each object. Please assume example below.
Assume the following hierarchy:
Country.State.City.Person
Example of usage I'm trying to achieve:
Guard.ThrowIfNull(Country.State.City.Person);
or
Guard.ThrowIfNull(() => Country.State.City.Person);
EXPECTED RESULT:
if Person is NULL, then throw ArgumentNullException("Country.State.City.Person")
if City is NULL, then throw ArgumentNullException("Country.State.City")
if State is NULL, then throw ArgumentNullException("Country.State")
if Country is NULL, then throw ArgumentNullException("Country")
At minimum, I believe you'll need to pass an instance to the Guard.ThrowIfNull function in addition to the path you want to check.
I wrote a proof of concept below that exhibits the behavior you describe. It does not handle collections, however. (In the case that State is a collection, this code will fail for "Country.State.City".)
Usage:
Guard.ThrowIfNull(country, "Country.State.City.Person");
// Or
Guard.ThrowIfNull(country, x => x.State.City.Person);
Code:
public static class Guard
{
/// <summary>
/// Throws an ArgumentNullException if any portion of a given property path is null.
/// </summary>
/// <typeparam name="T">The type to check.</typeparam>
/// <param name="instanceToCheck">The instance to check</param>
/// <param name="pathToCheck">The full path of the property to check. The path should include the name of the instance type.</param>
public static void ThrowIfNull<T>(T instanceToCheck, string pathToCheck)
{
var propsToCheck = pathToCheck?.Split('.');
if (propsToCheck?.Any() != true)
{
throw new ArgumentNullException(nameof(pathToCheck));
}
if (typeof(T).Name != propsToCheck.First())
{
throw new ArgumentException($"The type of {nameof(instanceToCheck)} does not match the given {nameof(pathToCheck)}.");
}
if (instanceToCheck == null)
{
throw new ArgumentNullException($"{pathToCheck.First()}");
}
object currentObj = instanceToCheck;
for (var i = 1; i < propsToCheck.Length; i++)
{
var prop = currentObj.GetType().GetProperties().FirstOrDefault(x => x.Name == propsToCheck[i]);
if (prop == null)
{
throw new ArgumentException($"The path, '{string.Join(".", propsToCheck.Take(i + 1))}' could not be found.");
}
currentObj = prop.GetValue(currentObj);
if (currentObj == null)
{
throw new ArgumentNullException($"{string.Join(".", propsToCheck.Take(i + 1))}");
}
}
}
/// <summary>
/// Throws an ArgumentNullException if any portion of a given property path is null.
/// </summary>
/// <typeparam name="T">The type to check.</typeparam>
/// <param name="instanceToCheck">The instance to check</param>
/// <param name="pathToCheck">The full path of the property to check.</param>
public static void ThrowIfNull<T, TProp>(T instanceToCheck, Expression<Func<T, TProp>> pathToCheck)
{
ThrowIfNull(instanceToCheck, (typeof(T).Name + pathToCheck.ToString().Substring(pathToCheck.ToString().IndexOf("."))));
}
}
I have Enum:
public enum SomeType
{
TypeA,
TypeB,
TypeC
}
but in MongoDB i would like this map to:
type_a type_b type_c
I'm using EnumRepresentationConvention(BsonType.String)
I tried:
public enum SomeType
{
[BsonElement("type_a")]
TypeA,
[BsonElement("type_b")]
TypeB,
[BsonElement("type_c")]
TypeC
}
but this doesn't work. Im getting exception:
Requested value 'type_a' was not found.
Is anyone know how can achieve such mapping in MongoDb C# driver?
UPDATE
So I wrote a new serializer that does what you need. I built it off some code I wrote as part of SharpExtensions. It certainly isn't optimized (or as simplified as it could be) but it works.
First I created a sample class Foo and reused your example Enum. I then leveraged the DescriptionAttribute to specify an alternate representation of the Enum that you completely control. Though this could be potentially simplified if you leverage something like Humanizer to consistently change the representation.
I then created a BsonSerializationProvider to let the driver know when it should use that serializer (much like my original answer). The meat is in EnumDescriptionSerializer which uses reflection to find the string representation of a particular value of SomeType. This is where I leverage the boilerplate code from SharpExtensions to move between the string and the actual Enum value. You'll notice the code will also work with EnumMemberAttribute as well as DescriptionAttribute. Please feel free to import the SharpExtensions library if you don't want to use the boilerplate code directly.
public class Foo
{
public ObjectId Id {get;set;}
public SomeType Enum {get;set;}
}
public enum SomeType
{
[Description("type_a")]
TypeA,
[Description("type_b")]
TypeB,
[Description("type_c")]
TypeC
}
public class EnumDescriptionSerializerProvider : BsonSerializationProviderBase
{
public override IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry registry)
{
if (!type.GetTypeInfo().IsEnum) return null;
var enumSerializerType = typeof(EnumDescriptionSerializer<>).MakeGenericType(type);
var enumSerializerConstructor = enumSerializerType.GetConstructor(new Type[0]);
var enumSerializer = (IBsonSerializer)enumSerializerConstructor?.Invoke(new object[0]);
return enumSerializer;
}
}
public class EnumDescriptionSerializer<TEnum> : StructSerializerBase<TEnum> where TEnum : struct
{
public BsonType Representation => BsonType.String;
public override TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var valAsString = context.Reader.ReadString();
var enumValue = valAsString.GetValueFromDescription<TEnum>();
return enumValue;
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TEnum value)
{
context.Writer.WriteString(value.GetDescription());
}
}
public static class EnumExtensions
{
public enum StringCase
{
/// <summary>
/// The default capitalization
/// </summary>
Default,
/// <summary>
/// Lower Case, ex. i like widgets.
/// </summary>
[Description("Lower Case")]
Lower,
/// <summary>
/// Upper Case, ex. I LIKE WIDGETS.
/// </summary>
[Description("Upper Case")]
Upper,
/// <summary>
/// Lower Camelcase, ex: iLikeWidgets.
/// </summary>
[Description("Lower Camelcase")]
LowerCamel,
/// <summary>
/// Upper Camelcase, ex: ILikeWidgets.
/// </summary>
[Description("Upper Camelcase")]
UpperCamel
}
/// <summary>
/// Get the value of an enum as a string.
/// </summary>
/// <param name="val"> The enum to convert to a <see cref="string"/>. </param>
/// <param name="case"> A <see cref="StringCase"/> indicating which case to return. Valid enumerations are StringCase.Lower and StringCase.Upper. </param>
/// <exception cref="ArgumentNullException"> If the enum is null. </exception>
/// <returns></returns>
public static string GetName<TEnum>(this TEnum val, StringCase #case = StringCase.Default) where TEnum : struct
{
var name = Enum.GetName(val.GetType(), val);
if (name == null) return null;
switch (#case)
{
case StringCase.Lower:
return name.ToLower();
case StringCase.Upper:
return name.ToUpper();
default:
return name;
}
}
/// <summary>
/// Gets the description for the supplied Enum Value.
/// </summary>
/// <param name="val">The value for which to get the description attribute.</param>
/// <returns>The <see cref="string"/> description.</returns>
public static string GetDescription<TEnum>(this TEnum val) where TEnum : struct
{
var fields = val.GetType().GetTypeInfo().GetDeclaredField(GetName(val));
// first try and pull out the EnumMemberAttribute, common when using a JsonSerializer
if (fields.GetCustomAttributes(typeof(EnumMemberAttribute), false).FirstOrDefault() is EnumMemberAttribute jsonAttribute) return jsonAttribute.Value;
// If that doesn't work, do the regular description, that still fails, just return a pretty ToString().
return !(fields.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() is DescriptionAttribute attribute) ? GetName(val) : attribute.Description;
}
/// <summary>
/// Get the value of an <see cref="Enum"/> based on its description attribute.
/// </summary>
/// <typeparam name="T">The type of the <see cref="Enum"/>.</typeparam>
/// <param name="description">The Description attribute of the <see cref="Enum"/>.</param>
/// <returns>The value of T or default(T) if the description is not found.</returns>
public static T GetValueFromDescription<T>(this string description) where T : struct
{
if (string.IsNullOrWhiteSpace(description)) throw new ArgumentNullException(nameof(description));
var type = typeof(T);
if (!type.GetTypeInfo().IsEnum) throw new ArgumentOutOfRangeException(nameof(T), $"{typeof(T)} is not an Enum.");
var fields = type.GetRuntimeFields();
foreach (var field in fields)
{
if (field.Name == description) return (T)field.GetValue(null);
// first try and pull out the EnumMemberAttribute, common when using a JsonSerializer
if (field.GetCustomAttribute(typeof(EnumMemberAttribute), false) is EnumMemberAttribute jsonAttribute && jsonAttribute.Value == description) return (T)field.GetValue(null);
// If that doesn't work, do the regular description, that still fails, just return a pretty ToString().
if (field.GetCustomAttribute(typeof(DescriptionAttribute), false) is DescriptionAttribute attribute && attribute.Description == description) return (T)field.GetValue(null);
}
throw new Exception($"Failed to parse value {description} into enum {typeof(T)}");
}
}
I wrote a simple test inserti
ng several Foo documents into a collection. This is how they look in the database
> db.enum.find()
{ "_id" : ObjectId("5c76c0240bba918778cc6b7f"), "Enum" : "type_a" }
{ "_id" : ObjectId("5c76c0580bba918778cc6b80"), "Enum" : "type_a" }
{ "_id" : ObjectId("5c76c05d0bba918778cc6b81"), "Enum" : "type_b" }
I also verified that they round-trip correctly. I haven't run any tests beyond some simple code using LINQPad. I believe this is what you are looking for.
ORIGINAL ANSWER
I wrote a custom serializer for this so I could register it and things "just work".
public class EnumAsStringSerializationProvider : BsonSerializationProviderBase
{
public override IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry registry)
{
if (!type.GetTypeInfo().IsEnum) return null;
var enumSerializerType = typeof(EnumSerializer<>).MakeGenericType(type);
var enumSerializerConstructor = enumSerializerType.GetConstructor(new[] { typeof(BsonType) });
var enumSerializer = (IBsonSerializer) enumSerializerConstructor?.Invoke(new object[] { BsonType.String });
return enumSerializer;
}
}
Then I register it with the BsonSerializer.
var enumAsStringSerializationProvider = new EnumAsStringSerializationProvider();
BsonSerializer.RegisterSerializationProvider(enumAsStringSerializationProvider);
For me, it just works and I don't need to remember to decorate enums.
I have a Dictionary containing strings as keys, and objects as values in an abstract class.
I have two classes deriving from this abstract class.
One of the deriving classes works perfectly, all configurations and items are loaded and retrievable without issues.
However, the other class is giving me headaches.
When I try to get an object of type "Domain"; I get an invalid cast exception, although I am adding the value to the dictionary as said type.
Here is the code:
public sealed class UserDomainConfig: ConfigParser {
public UserDomainConfig(string configFilePath) : base(configFilePath) { }
public Domain GetConfig(string key) => GetConfig<Domain>(key);
public override bool LoadConfigs() {
return base.LoadConfigs();
}
public UserDomainConfig SetConfig(string key, Domain value) {
base.SetConfig(key, value);
return this;
}
}
public abstract class ConfigParser: IConfig {
/* Snip */
/// <summary>
/// Gets the config.
/// </summary>
/// <returns>The config.</returns>
/// <param name="key">Key.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public virtual T GetConfig<T>(string key) {
object output = null;
try {
if (!configs.TryGetValue(key, out output))
return default(T);
//return (T)output;
//return output as T;
// This is where the exception is occurring.
// I've tried multiple solutions to try to remedy this issue.
return (T)Convert.ChangeType(output, typeof(T));
} catch (InvalidCastException ex) {
logger.Error($"Failed to cast config { key }!");
}
return default(T);
}
/// <summary>
/// Sets the config.
/// </summary>
/// <returns>The config.</returns>
/// <param name="key">Key.</param>
/// <param name="value">Value.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public virtual IConfig SetConfig<T>(string key, T value) {
if (KeyExists(key))
configs.Remove(key);
configs.Add(key, value);
return this;
}
Any ideas on how to fix this, and/or why this isn't working in the first place, although it works like a charm with strings, bools, and ints?
The Convert class only supports simple types, known by .NET, like Int32, String, DateTime. It does not support user defined or complex types like Domain. If you try to convert something to a not-supported type, the method Convert.ChangeType throws an InvalidCastException. The only exception is that it will work if the Original value (output) is already of that desired type; than no actual conversion is needed.
For more information, read: https://msdn.microsoft.com/en-us/library/dtb69x08(v=vs.110).aspx
If you are certain the stored value is of the type Domain, than log more information. Something like this:
logger.Error($"Failed to cast config { key } of type { output.GetType() } to type { typeof(T) }!");
This way you can verify your claim that both types are the same.
I need to know how to convert an int to a nullable int. However, I keep getting an error "The binary operator Equal is not defined for the types 'System.Nullable`1[System.Int32]' and 'System.Int32'." Any solution. It needs to be Microsoft SQL Server's nullable int type.
somevalue = Expression.Constant(something.GetValue(some,null).To<Nullable<System.Int32>> ());
public static T To<T>(this object obj)
{
Type t = typeof(T);
Type u = Nullable.GetUnderlyingType(t);
if (u != null)
{
if (obj == null)
return default(T);
return (T)Convert.ChangeType(obj, u);
}
else
{
return (T)Convert.ChangeType(obj, t);
}
}'
Typically, you convert an int an int? using a cast.
int? myNullable = (int?) 15;
int myInt = (int) myNullable;
That To code seems to be you trying to construct a Constant of nullable type when given a value of non-nullable type but that is not at all the right way to go about this. The way you're trying to do this indicates that you have a misunderstanding about how boxed value types work.
That error message indicates that you are constructing a binary operator expression tree node which has as its operands an expression node of nullable int type and an expression node of int type. That's not legal; they have to be both nullable int. What you should be doing is wrapping the non-nullable int expression tree node in a Convert expression tree node which converts it to a nullable int, and then pass that to the binary operator expression tree node constructor.
That is, this is wrong:
var someIntExpr = Expression.Constant(123, typeof(int));
var someNubIntExpr = Expression.Constant(null, typeof(int?));
var badEq = Expression.Equal(someIntExpr, someNubIntExpr);
This is right:
var goodEq = Expression.Equal(Expression.Convert(someIntExpr, typeof(int?)), someNubIntExpr);
So why is what you're doing wrong?
You have a method To<T> which returns a T. It correctly takes in an int and returns the equivalent int?. So then what? You pass that to Expression.Constant, which boxes the nullable int into a boxed int, and then makes a constant out of that. You believe that there is such a thing as a boxed nullable value type, but there is not! A nullable value type boxes either to a null reference or to a boxed non-nullable value type.
So you could also solve your problem by not doing any of this crazy stuff in the first place. If you have a boxed int in hand, and you need a constant expression tree node of nullable type, just provide the type.
Expression.Constant(someBoxedIntValue, typeof(int?))
Done. So: wrapping up, you have two solutions:
If you have a boxed int in hand, pass it and the nullable value type you want to the Constant factory, or
if you have an expression node of type int in hand then use the Convert expression node factory, and pass it and the desired type to that.
Both will give you back an expression node of the correct type to be compared to another nullable int.
int test = 0; // set int
int? num = test; // convert test to a nullable int
num = null; // set num as null
int i = 1;
int? k;
k = i as int?;
Like this you will convert i which is an int to a nullable int ;)
int? is the short version of Nullable<int>.
Does something simpler like this not work?
int i;
int? temp = int.TryParse(<your value>, out i) ? (int?)i : null;
Here you go. A generic string to nullable primitive solution.
int? n = " 99 ".ToNullable<int>();
/// <summary>
/// Developed by Taylor Love
/// </summary>
public static class ToNullableStringExtension
{
/// <summary>
/// <para>More convenient than using T.TryParse(string, out T).
/// Works with primitive types, structs, and enums.
/// Tries to parse the string to an instance of the type specified.
/// If the input cannot be parsed, null will be returned.
/// </para>
/// <para>
/// If the value of the caller is null, null will be returned.
/// So if you have "string s = null;" and then you try "s.ToNullable...",
/// null will be returned. No null exception will be thrown.
/// </para>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="p_self"></param>
/// <returns></returns>
public static T? ToNullable<T>(this string p_self) where T : struct
{
if (!string.IsNullOrEmpty(p_self))
{
var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
}
return null;
}
https://github.com/Pangamma/PangammaUtilities-CSharp/tree/master/src/StringExtensions
This question already has answers here:
how to get the default value of a type if the type is only known as System.Type? [duplicate]
(3 answers)
Closed 9 years ago.
For any given type i want to know its default value.
In C#, there is a keyword called default for doing this like
object obj = default(Decimal);
but I have an instance of Type (called myType) and if I say this,
object obj = default(myType);
it doesn't work
Is there any good way of doing this?
I know that a huge switch block will work but thats not a good choice.
There's really only two possibilities: null for reference types and new myType() for value types (which corresponds to 0 for int, float, etc) So you really only need to account for two cases:
object GetDefaultValue(Type t)
{
if (t.IsValueType)
return Activator.CreateInstance(t);
return null;
}
(Because value types always have a default constructor, that call to Activator.CreateInstance will never fail).
You could also add it as an extension method to System.Type:
public static class TypeExtensions
{
public static object GetDefaultValue(this Type t)
{
if (t.IsValueType && Nullable.GetUnderlyingType(t) == null)
return Activator.CreateInstance(t);
else
return null;
}
}
Having solved this problem in my own systems, here is a method for correctly determining the default value of an arbitrary Type at run time, which has been tested against thousands of Types:
/// <summary>
/// [ <c>public static object GetDefault(this Type type)</c> ]
/// <para></para>
/// Retrieves the default value for a given Type
/// </summary>
/// <param name="type">The Type for which to get the default value</param>
/// <returns>The default value for <paramref name="type"/></returns>
/// <remarks>
/// 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.
/// </remarks>
/// <example>
/// To use this method in its native, non-extension form, make a call like:
/// <code>
/// object Default = DefaultValue.GetDefault(someType);
/// </code>
/// To use this method in its Type-extension form, make a call like:
/// <code>
/// object Default = someType.GetDefault();
/// </code>
/// </example>
/// <seealso cref="GetDefault<T>"/>
public static object GetDefault(this 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 <" + 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/enum), 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 <" + type +
"> (Inner Exception message: \"" + e.Message + "\")", e);
}
}
// Fail with exception
throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> is not a publicly-visible type, so the default value cannot be retrieved");
}
In these examples, the GetDefault method is implemented in the static class DefaultValue. Call this method with a statement like:
object Default = DefaultValue.GetDefault(someType);
To use the GetDefault method as an extension method for Type, call it like this:
object Default = someType.GetDefault();
This second, Type-extension approach is a simpler client-code syntax, since it removes the need to reference the containing DefaultValue class qualifier on the call.
The above run time form of GetDefault works with identical semantics as the primitive C# 'default' keyword, and produces the same results.
To use a generic form of GetDefault, you may access the following function:
/// <summary>
/// [ <c>public static T GetDefault< T >()</c> ]
/// <para></para>
/// Retrieves the default value for a given Type
/// </summary>
/// <typeparam name="T">The Type for which to get the default value</typeparam>
/// <returns>The default value for Type T</returns>
/// <remarks>
/// 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.
/// </remarks>
/// <seealso cref="GetDefault(Type)"/>
public static T GetDefault<T>()
{
return (T) GetDefault(typeof(T));
}
A call to the generic form could be something like:
int? inDefaultVal = DefaultValue.GetDefault<int?>();
Of course, the above generic form of GetDefault is unnecessary for C#, since it works the same as default(T). It is only useful for a .NET language that does not support the 'default' keyword but which supports generic types. In most cases, the generic form is unnecessary.
A useful corollary method is one to determine whether an object contains the default value for its Type. I also rely on the following IsObjectSetToDefault method for that purpose:
/// <summary>
/// [ <c>public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue)</c> ]
/// <para></para>
/// Reports whether a value of type T (or a null reference of type T) contains the default value for that Type
/// </summary>
/// <remarks>
/// Reports whether the object is empty or unitialized for a reference type or nullable value type (i.e. is null) or
/// whether the object contains a default value for a non-nullable value type (i.e. int = 0, bool = false, etc.)
/// <para></para>
/// NOTE: For non-nullable value types, this method introduces a boxing/unboxing performance penalty.
/// </remarks>
/// <param name="ObjectType">Type of the object to test</param>
/// <param name="ObjectValue">The object value to test, or null for a reference Type or nullable value Type</param>
/// <returns>
/// true = The object contains the default value for its Type.
/// <para></para>
/// false = The object has been changed from its default value.
/// </returns>
public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue)
{
// If no ObjectType was supplied, attempt to determine from ObjectValue
if (ObjectType == null)
{
// If no ObjectValue was supplied, abort
if (ObjectValue == null)
{
MethodBase currmethod = MethodInfo.GetCurrentMethod();
string ExceptionMsgPrefix = currmethod.DeclaringType + " {" + currmethod + "} Error:\n\n";
throw new ArgumentNullException(ExceptionMsgPrefix + "Cannot determine the ObjectType from a null Value");
}
// Determine ObjectType from ObjectValue
ObjectType = ObjectValue.GetType();
}
// Get the default value of type ObjectType
object Default = ObjectType.GetDefault();
// If a non-null ObjectValue was supplied, compare Value with its default value and return the result
if (ObjectValue != null)
return ObjectValue.Equals(Default);
// Since a null ObjectValue was supplied, report whether its default value is null
return Default == null;
}
The above IsObjectSetToDefault method can either be called in its native form or accessed as a Type-class extension.
How about something like...
class Program
{
static void Main(string[] args)
{
PrintDefault(typeof(object));
PrintDefault(typeof(string));
PrintDefault(typeof(int));
PrintDefault(typeof(int?));
}
private static void PrintDefault(Type type)
{
Console.WriteLine("default({0}) = {1}", type,
DefaultGenerator.GetDefaultValue(type));
}
}
public class DefaultGenerator
{
public static object GetDefaultValue(Type parameter)
{
var defaultGeneratorType =
typeof(DefaultGenerator<>).MakeGenericType(parameter);
return defaultGeneratorType.InvokeMember(
"GetDefault",
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.InvokeMethod,
null, null, new object[0]);
}
}
public class DefaultGenerator<T>
{
public static T GetDefault()
{
return default(T);
}
}
It produces the following output:
default(System.Object) =
default(System.String) =
default(System.Int32) = 0
default(System.Nullable`1[System.Int32]) =
What do you mean by "Default Value"? All reference Types ("class") have null as default value, while all value types will have their default values according to this table.
Here's a function that will return the default value for a nullable type (in other words, it returns 0 for both Decimal and Decimal?):
public static object DefaultValue(Type maybeNullable)
{
Type underlying = Nullable.GetUnderlyingType(maybeNullable);
if (underlying != null)
return Activator.CreateInstance(underlying);
return Activator.CreateInstance(maybeNullable);
}