Using SetValue with implicit conversion - c#

Essentially, I'm trying to use
field.SetValue(obj, val);
Where val's type can be implicitly converted to the true field type, but isn't 'directly' assignable. Of course, I get the usual ArgumentException: Object type cannot be converted to target type. Is there a way to do this without manually finding and calling the constructor?

Try with Convert.ChangeType:
field.SetValue(obj, Convert.ChangeType(val, field.PropertyType), null);
Check the following Setting a property by reflection with a string value for further info.

This is a quite complicated question, and I don't know a way to make it work in a sigle line. While the Convert.ChangeType works for simple cases, it will fail in the following cases :
The target type is a Nullable enum, and you use an integer value (not an enum value) : in that case, you'l need use Enum.ToObject to make it work.
Your value is DBNull.Value : You will need to test it and assign null in that case
The target type is not the same type of number as the value you want to set : Convert.ChangeType will help you here.
Here's a sample that illustrates how to do it :
public void SetFieldValue(FieldInfo field, object targetObj, object value)
{
object valueToSet;
if (value == null || value == DBNull.Value)
{
valueToSet = null;
}
else
{
Type fieldType = field.FieldType;
//assign enum
if (fieldType.IsEnum)
valueToSet = Enum.ToObject(fieldType, value);
//support for nullable enum types
else if (fieldType.IsValueType && IsNullableType(fieldType))
{
Type underlyingType = Nullable.GetUnderlyingType(fieldType);
valueToSet = underlyingType.IsEnum ? Enum.ToObject(underlyingType, value) : value;
}
else
{
//we always need ChangeType, it will convert the value to the proper number type, for example.
valueToSet = Convert.ChangeType(value, fieldType);
}
}
field.SetValue(targetObj, valueToSet);
}
A unit test for the function :
enum TestEnum
{
DummyValue
}
class TestClass
{
public int IntValue;
public decimal DecimalValue;
public int? NullableInt;
public TestEnum EnumValue;
public TestEnum? NullableEnumValue;
public TestClass ObjectValue;
}
[TestFixture]
public class DataObjectBinderFixture
{
private TestClass _testObject;
private void SetFieldValue(string fieldName, object value)
{
var fieldInfo = typeof (TestClass).GetField(fieldName);
ReflectionUtils.SetFieldValue(fieldInfo, _testObject, value);
}
[Test]
public void TestSetValue()
{
_testObject = new TestClass();
SetFieldValue("IntValue", 2.19);
SetFieldValue("IntValue", DBNull.Value);
SetFieldValue("DecimalValue", 1);
SetFieldValue("NullableInt", null);
SetFieldValue("NullableInt", 12);
SetFieldValue("EnumValue", TestEnum.DummyValue);
SetFieldValue("EnumValue", 0);
SetFieldValue("NullableEnumValue", TestEnum.DummyValue);
SetFieldValue("NullableEnumValue", null);
SetFieldValue("NullableEnumValue", 0);
SetFieldValue("NullableEnumValue", DBNull.Value);
SetFieldValue("ObjectValue", DBNull.Value);
}
}

Related

Default value of DateTime is not working as expected

I have a class with DateTime and DateTime? variables,"if (value != defaultValue)" should fail and the control should not go inside. but then here in my case it is executing.
(value != defaultValue) is not working. what can I replace the code with.
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
DateClass v = new DateClass();
v.Date1 = default;
v.Date1 = default;
foreach (PropertyInfo pi in v.GetType().GetProperties())
{
var type = pi.PropertyType;
var value = pi.GetValue(v);
var defaultValue = GetDefault(type);
if (value != defaultValue)
{
Console.WriteLine("not equal");
}
else
{
Console.WriteLine("equal");
}
}
}
public static object GetDefault(Type type)
{
if (type.IsValueType)
{
var k = Activator.CreateInstance(type);
return k;
}
return null;
}
}
public class DateClass
{
public DateTime Date1
{
get; set;
}
public DateTime? Date2
{
get; set;
}
}
}
The problem is that the compile-time type of PropertyInfo.GetValue is object... which means that basically you're boxing the DateTime, and then comparing two different references, with a reference comparison.
Personally I'd say that a field with a value of DateTime.MinValue (aka default(DateTime) is still non-null (as is an int field with a value of 0, for example), and that you should either change the name of the method or only exclude genuine null values. However, if you do want to down this route, I'd change your condition to just
if (!Equals(value, defaultValue))
That will perform a value equality check, which is what you want.

How to get an IEnumerable<string> of enum values attributes?

I have a StringValue attribute for enums values, so I could attach a description to each value:
public class StringValueAttribute : Attribute
{
public string Value { get; private set; }
public StringValueAttribute(string value)
{
Value = value;
}
}
And that is how I use it:
enum Group
{
[StringValue("Computer Science")]
ComputerScience,
[StringValue("Software Engineering")]
SoftwareEngineering,
// ... additional values follow.
}
I have a method that retrieves the StringValue given the enum value:
public static string GetStringValue(Enum value)
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(type.ToString());
StringValueAttribute[] attributes = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
string stringValue = null;
if (attributes.Length > 0)
{
stringValue = attributes[0].Value;
}
return stringValue;
}
I want to have another method that gets an enum (the enum itself, not a value) and retrieves an IEnumerable using the GetStringValue method. I am not sure how to accomplish that. How could a method like this look like?
Edit: this question is not a duplicate of How to get C# Enum description from value?. I know how to get an enum attribute value, and I actually have a method in the question that does exactly that. My question is how to enumerate all the attributes in an enum.
The most straightforward way to do this is with a generic, though you could always pass in an instance of your specific Enum, get its type, then return the StringValue values for all its values:
public static class EnumExtensions
{
public static IEnumerable<string> GetStringValues<TEnum>() where TEnum : struct, IConvertible, IComparable, IFormattable
{
return Enum.GetValues(typeof(TEnum))
.Cast<Enum>()
.Select(e => e.GetStringValue())
.ToList();
}
public static IEnumerable<string> GetStringValuesOfType(Enum value)
{
return Enum.GetValues(value.GetType())
.Cast<Enum>()
.Select(e => e.GetStringValue())
.ToList();
}
public static string GetStringValue(this Enum value)
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
StringValueAttribute[] attributes = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
string stringValue = null;
if (attributes.Length > 0)
{
stringValue = attributes[0].Value;
}
return stringValue;
}
}
Notes:
There is no where TEnum : Enum constraint in c#. Restricting TEnum to be struct, IConvertible, IComparable, IFormattable is mostly sufficient.
That being said, there is a cunning trick to apply an enum constraint which is shown in this answer to Enum type constraints in C# by SLaks. (I didn't use it in this answer though since it's really very cunning.)
As noted in #EdPlunkett's comment you need to pass value.ToString() to type.GetField() since you're getting the field corresponding to that specific incoming enum value.
Sample fiddle
This should work:
static void Main(string[] args)
{
foreach (var item in GetStringNames<Group>())
{
Console.WriteLine(item);
}
}
public static string GetStringValue(Enum value)
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
StringValueAttribute[] attributes = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
string stringValue = null;
if (attributes.Length > 0)
{
stringValue = attributes[0].Value;
}
return stringValue;
}
public static IEnumerable<string> GetStringNames<T>()
{
var type = typeof(T);
if (type.IsEnum == false)
{
throw new ArgumentException("T must be an Enum type");
}
var values = type.GetEnumValues();
foreach (var item in values)
{
yield return GetStringValue((Enum)item);
}
}

ASP.NET C# Error: The Type must be a reference type in order to use it as parameter 'T' in the generic type or method

I have a class: Constants.cs
Code:
namespace DataAccess.Utilities
{
public class Constants
{
public enum ReturnCode
{
Fail = -1,
Success = 0,
Warning = 1
}
}
}
This is my code for directcast class
public static T DirectCast<T>(object o) where T : class
{
T value = o as T;
if (value == null && o != null)
{
throw new InvalidCastException();
}
return value;
}
Here is my code that get's error
code = DirectCast<Constants.ReturnCode>(int.Parse(db.GetParameterValue(command, "RESULTCODE").ToString().Trim()));
Error Message:
Error 2 The type 'DataAccess.Utilities.Constants.ReturnCode' must be a reference type in order to use it as parameter 'T' in the generic type or method 'DataAccess.DataManager.QueueingManager.DirectCast(object)'
Before I am using DirectCast from .net vb, but DirectCast is not exist in c# so I try searching and I get some codes on how to create DirectCast that has same function to vb.
In .NET, there are value types and reference types. Classes are reference types for example, they are like pointers in C++.
int are value types, enums also.
In generics, only reference types can be use as type arguments .
You can remove the genericity of your method but you won't be able to know the return type. Moreover, "as" won't work with enum because it's a value type.
I don't understand why you want to cast an enum, you can parse it :
Enum.Parse(typeof(Constants.ReturnCode), db.GetParameterValue(command, "RESULTCODE").ToString().Trim()));
code = DirectCast<Constants.ReturnCode>(int.Parse(db.GetParameterValue(command, "RESULTCODE").ToString().Trim()));
Should be changed to:
code = DirectCast<Constants>(int.Parse(db.GetParameterValue(command, "RESULTCODE").ToString().Trim()));
Or, do it like this: see working example:
class Program
{
static void Main(string[] args)
{
var fail = Constants.DirectCast<Constants.ReturnCode>(-1);
var success = Constants.DirectCast<Constants.ReturnCode>(0);
var warning = Constants.DirectCast<Constants.ReturnCode>(1);
Console.WriteLine(fail);
Console.WriteLine(success);
Console.WriteLine(warning);
Console.ReadLine();
}
}
public class Constants
{
public enum ReturnCode
{
Fail = -1,
Success = 0,
Warning = 1
}
public static T DirectCast<T>(object o)
{
T value = (T)o;
if (value == null && o != null)
{
throw new InvalidCastException();
}
return value;
}
}
Thanks for the comments. finally my code works.
public class Constants
{
public enum ReturnCode
{
Fail = -1,
Success = 0,
Warning = 1
}
}
public static T DirectCast<T>(object o)
{
T value= (T)o;
if (value== null && o != null)
{
throw new InvalidCastException();
}
return value;
}
code = Constants.DirectCast<Constants.ReturnCode>(int.Parse(db.GetParameterValue(command, "RESULTCODE").ToString().Trim()));

Check to see if a given object (reference or value type) is equal to its default

I'm trying to find a way to check and see if the value of a given object is equal to its default value. I've looked around and come up with this:
public static bool IsNullOrDefault<T>(T argument)
{
if (argument is ValueType || argument != null)
{
return object.Equals(argument, default(T));
}
return true;
}
The problem I'm having is that I want to call it like this:
object o = 0;
bool b = Utility.Utility.IsNullOrDefault(o);
Yes o is an object, but I want to make it figure out the base type and check the default value of that. The base type, in this case, is an integer and I want to know in this case if the value is equal to default(int), not default(object).
I'm starting to think this might not be possible.
In your example, your integer is boxed and therefore your T is going to be object, and the default of object is null, so that's not valuable to you. If the object is a value type, you could get an instance of it (which would be the default) to use as a comparison. Something like:
if (argument is ValueType)
{
object obj = Activator.CreateInstance(argument.GetType());
return obj.Equals(argument);
}
You'd want to deal with other possibilities before resorting to this. Marc Gravell's answer brings up some good points to consider, but for a full version of your method, you might have
public static bool IsNullOrDefault<T>(T argument)
{
// deal with normal scenarios
if (argument == null) return true;
if (object.Equals(argument, default(T))) return true;
// deal with non-null nullables
Type methodType = typeof(T);
if (Nullable.GetUnderlyingType(methodType) != null) return false;
// deal with boxed value types
Type argumentType = argument.GetType();
if (argumentType.IsValueType && argumentType != methodType)
{
object obj = Activator.CreateInstance(argument.GetType());
return obj.Equals(argument);
}
return false;
}
if o is null, in a non-generic (object) method, you will have no access to the original type - and you can't do much about that.
Hence, the only time it matters is non-nullable value-types, so:
Type type = value.GetType();
if(!type.IsValueType) return false; // can't be, as would be null
if(Nullable.GetUnderlyingType(type) != null) return false; // ditto, Nullable<T>
object defaultValue = Activator.CreateInstance(type); // must exist for structs
return value.Equals(defaultValue);
Converted Anthony Pegram's Answer into an extension method:
using System;
//Adapted from https://stackoverflow.com/a/6553276/1889720
public static class ObjectExtensions
{
public static bool IsNullOrDefault<TObject>(this TObject argument)
{
// deal with normal scenarios
if (argument == null)
{
return true;
}
if (object.Equals(argument, default(TObject)))
{
return true;
}
// deal with non-null nullables
Type methodType = typeof(TObject);
if (Nullable.GetUnderlyingType(methodType) != null)
{
return false;
}
// deal with boxed value types
Type argumentType = argument.GetType();
if (argumentType.IsValueType && argumentType != methodType)
{
object obj = Activator.CreateInstance(argument.GetType());
return obj.Equals(argument);
}
return false;
}
}
Usage syntax:
myVariable.IsNullOrDefault();
Expanding on Marc Gravell's answer, by getting the run-time type as an argument:
// Handles boxed value types
public static bool IsNullOrDefault([CanBeNull] this object #object,
[NotNull] Type runtimeType)
{
if (#object == null) return true;
if (runtimeType == null) throw new ArgumentNullException("runtimeType");
// Handle non-null reference types.
if (!runtimeType.IsValueType) return false;
// Nullable, but not null
if (Nullable.GetUnderlyingType(runtimeType) != null) return false;
// Use CreateInstance as the most reliable way to get default value for a value type
object defaultValue = Activator.CreateInstance(runtimeType);
return defaultValue.Equals(#object);
}
For those of you that will challenge my use case, I want to list the values of properties on an arbitrary object, omitting properties which set to their defaults (for a more concise display).
Because propertyInfo.GetValue(targetObject, null) returns an object, and value types are boxed, I cannot use a generic method. By passing propertyInfo.PropertyType as the second parameter to this method I can avoid the problem a generic method has with boxed value types.
The following will sort it out.
public static bool IsNullOrDefault<T>(T argument)
{
if (argument is ValueType || argument != null)
{
return object.Equals(argument, GetDefault(argument.GetType()));
}
return true;
}
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
Make an extension method
public static class DateExtension
{
public static bool IsNullOrDefault(this DateTime? value)
{
return default(DateTime) == value || default(DateTime?) == value;
}
}
Solution with linq expressions. First call for type will be relatively slow, but then it should work just as quick as usual code.
public static class DefaultHelper
{
private delegate bool IsDefaultValueDelegate(object value);
private static readonly ConcurrentDictionary<Type, IsDefaultValueDelegate> Delegates
= new ConcurrentDictionary<Type, IsDefaultValueDelegate>();
public static bool IsDefaultValue(this object value)
{
var type = value.GetType();
var isDefaultDelegate = Delegates.GetOrAdd(type, CreateDelegate);
return isDefaultDelegate(value);
}
private static IsDefaultValueDelegate CreateDelegate(Type type)
{
var parameter = Expression.Parameter(typeof(object));
var expression = Expression.Equal(
Expression.Convert(parameter, type),
Expression.Default(type));
return Expression.Lambda<IsDefaultValueDelegate>(expression, parameter).Compile();
}
}

Creating a generic method in C#

I am trying to combine a bunch of similar methods into a generic method. I have several methods that return the value of a querystring, or null if that querystring does not exist or is not in the correct format. This would be easy enough if all the types were natively nullable, but I have to use the nullable generic type for integers and dates.
Here's what I have now. However, it will pass back a 0 if a numeric value is invalid, and that unfortunately is a valid value in my scenarios. Can somebody help me out? Thanks!
public static T GetQueryString<T>(string key) where T : IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
What if you specified the default value to return, instead of using default(T)?
public static T GetQueryString<T>(string key, T defaultValue) {...}
It makes calling it easier too:
var intValue = GetQueryString("intParm", Int32.MinValue);
var strValue = GetQueryString("strParm", "");
var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // eg use today's date if not specified
The downside being you need magic values to denote invalid/missing querystring values.
I know, I know, but...
public static bool TryGetQueryString<T>(string key, out T queryString)
What about this? Change the return type from T to Nullable<T>
public static Nullable<T> GetQueryString<T>(string key) where T : struct, IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
Convert.ChangeType() doesn't correctly handle nullable types or enumerations in .NET 2.0 BCL (I think it's fixed for BCL 4.0 though). Rather than make the outer implementation more complex, make the converter do more work for you. Here's an implementation I use:
public static class Converter
{
public static T ConvertTo<T>(object value)
{
return ConvertTo(value, default(T));
}
public static T ConvertTo<T>(object value, T defaultValue)
{
if (value == DBNull.Value)
{
return defaultValue;
}
return (T) ChangeType(value, typeof(T));
}
public static object ChangeType(object value, Type conversionType)
{
if (conversionType == null)
{
throw new ArgumentNullException("conversionType");
}
// if it's not a nullable type, just pass through the parameters to Convert.ChangeType
if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
// null input returns null output regardless of base type
if (value == null)
{
return null;
}
// it's a nullable type, and not null, which means it can be converted to its underlying type,
// so overwrite the passed-in conversion type with this underlying type
conversionType = Nullable.GetUnderlyingType(conversionType);
}
else if (conversionType.IsEnum)
{
// strings require Parse method
if (value is string)
{
return Enum.Parse(conversionType, (string) value);
}
// primitive types can be instantiated using ToObject
else if (value is int || value is uint || value is short || value is ushort ||
value is byte || value is sbyte || value is long || value is ulong)
{
return Enum.ToObject(conversionType, value);
}
else
{
throw new ArgumentException(String.Format("Value cannot be converted to {0} - current type is " +
"not supported for enum conversions.", conversionType.FullName));
}
}
return Convert.ChangeType(value, conversionType);
}
}
Then your implementation of GetQueryString<T> can be:
public static T GetQueryString<T>(string key)
{
T result = default(T);
string value = HttpContext.Current.Request.QueryString[key];
if (!String.IsNullOrEmpty(value))
{
try
{
result = Converter.ConvertTo<T>(value);
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
You can use sort of Maybe monad (though I'd prefer Jay's answer)
public class Maybe<T>
{
private readonly T _value;
public Maybe(T value)
{
_value = value;
IsNothing = false;
}
public Maybe()
{
IsNothing = true;
}
public bool IsNothing { get; private set; }
public T Value
{
get
{
if (IsNothing)
{
throw new InvalidOperationException("Value doesn't exist");
}
return _value;
}
}
public override bool Equals(object other)
{
if (IsNothing)
{
return (other == null);
}
if (other == null)
{
return false;
}
return _value.Equals(other);
}
public override int GetHashCode()
{
if (IsNothing)
{
return 0;
}
return _value.GetHashCode();
}
public override string ToString()
{
if (IsNothing)
{
return "";
}
return _value.ToString();
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
public static explicit operator T(Maybe<T> value)
{
return value.Value;
}
}
Your method would look like:
public static Maybe<T> GetQueryString<T>(string key) where T : IConvertible
{
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
return new Maybe<T>();
}
}
return new Maybe<T>();
}
I like to start with a class like this
class settings
{
public int X {get;set;}
public string Y { get; set; }
// repeat as necessary
public settings()
{
this.X = defaultForX;
this.Y = defaultForY;
// repeat ...
}
public void Parse(Uri uri)
{
// parse values from query string.
// if you need to distinguish from default vs. specified, add an appropriate property
}
This has worked well on 100's of projects. You can use one of the many other parsing solutions to parse values.

Categories