For view model validation, I need to determine whether a value, of which I only have an object interface, is an enum and has the numeric value of -1.
I tried this:
// object value;
if (value?.GetType().IsEnum == true && (int)value == -1) return null;
It should work with my Model enums which are mostly based on int.
But it fails when the value is a Visibility enum (that happens to be also in the view model class and should be ignored for validation) which is based on byte instead of int and that seems not to be castable to int. I could do some more testing but it shouldn't get too slow.
Is there a good simple solution for that? Maybe some test method in the Enum class or something?
You can check the underlying type with GetEnumUnderlyingType():
Type t = value?.GetType();
if (t?.IsEnum == true &&
t?.GetEnumUnderlyingType() == typeof(int) &&
(int)value == -1)
return null;
Since a byte can never be -1, you don't need to check it. But you may need to extend the check for long enums, too.
UPDATE:
I just tried around a little and found that Convert.ToInt32() also solves your problem:
if (value?.GetType().IsEnum == true &&
Convert.ToInt64(value) == -1)
return null;
This seems cleaner and also works for all possible underlying types.
Another update: Unfortunatly the solution above is not as clean as I thought. Even with Convert.ToInt64() solves the problem of long values too big for Int32, but it throws if you pass for example a ulong.MaxValue.
So you have to choose a type that is large enough for all possible enum base types:
if (value?.GetType().IsEnum == true &&
Convert.ToDecimal(value) == -1)
return null;
Using Convert.ToDecimal() this passes all the test cases that came up so far.
In addition, one could consider using the Enum.IsDefined Method (Type, Object) to verify, if the value is a valid enum.
That does of course not cover the "check if it is -1" part.
Enum.IsDefined Method (Type, Object)
Returns an indication whether a constant with a specified value exists
in a specified enumeration. Namespace: System Assembly: mscorlib
(in mscorlib.dll)
[ComVisibleAttribute(true)] public static bool IsDefined( Type enumType, object value )
Parameters
enumTypeType: System.Type
An enumeration type.
valueType: System.Object The value or name of a constant in enumType.
Return Value
Type: System.Boolean becomes true if a constant in enumType has a
value equal to value; otherwise, false.
Exceptions
ArgumentNullException' enumType or value is null.
ArgumentException` enumType is not an Enum. -or- The type of value is an enumeration, but it is not an enumeration of type enumType. -or- The type of value is not an underlying type of enumType.
InvalidOperationException value is not type SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, UInt64, or String.
Remarks
The value parameter can be any of the following:
Any member of type enumType.
A variable whose value is an enumeration member of type enumType.
The string representation of the name of an enumeration member. The
characters in the string must have the same case as the enumeration
member name.
A value of the underlying type of enumType.
If the constants in enumType define a set of bit fields and value
contains the values, names, or underlying values of multiple bit
fields, the IsDefined method returns false. In other words, for
enumerations that define a set of bit fields, the method determines
only whether a single bit field belongs to the enumeration. To
determine whether multiple bit fields are set in an enumeration type
that is tagged with the FlagsAttribute attribute, you can call the
HasFlag method.
You could:
if (value != null)
{
var type = value.GetType();
if (type.IsEnum && object.Equals(Enum.ToObject(type, -1), value))
{
return null;
}
}
I had to do it in multiple lines to "cache" the GetType(). The trick is using Enum.ToObject() to "cast" -1 to the current Enum type.
Note that casting will generate "strange" results:
public enum TestEnum : ulong
{
ValueMax = ulong.MaxValue
}
In this case, TestEnum.ValueMax will return null. This will happen for all the unsigned types.
byte can be casted to int if it is not boxed with object
byte b = 1;
int i = (int)b; //works good
int f = (int)(object)b; //fails
So you could convert your variable to int using Convert.ToInt32 method, as René Vogt suggested, or cast it to dynamic instead casting to int:
if (value?.GetType().IsEnum == true && (dynamic)value == -1) return null;
Although, operations with dynamic are rather slow. In my opinion, soultion with Convert.ToInt32 is the most cleanable and efficient. This answer is just to point out, why you can not cast the object to int and suggest a dynamic version of cast.
Related
I have Type and I want to get the default value. For example, if Type is class or nullable I should get null. But if it is integer, DateTime. decimal, ... I should get 0. What is the best way of doing this?
From your description you can just use a default expression or literal.
int a = default(int);
int a = default;
Default values of C# types (C# reference)
Type
Default value
Any reference type
null
Anybuilt-in integral numeric type
0 (zero)
Any built-in floating-point numeric type
0 (zero)
bool
false
char
'\0' (U+0000)
enum
The value produced by the expression (E)0, where E is the enum identifier.
struct
The value produced by setting all value-type fields to their default values and all reference-type fields to null.
Any nullable value type
An instance for which the HasValue property is false and the Value property is undefined. That default value is also known as the null value of a nullable value type.
I expect you can use something like this:
public static object GetDefaultValue(Type type)
{
return type.IsClass ? null : Activator.CreateInstance(type);
}
Try it online
You could use Activator.CreateInstance to generate an empty variable of the type and then check the value of it :)
The method below looks for a particular key in a dictionary and attempts to safely store it in destination if it can. The problem I am running into is when T=Int64 and the item in the dictionary is Int32 or UInt32 (both of which can fit inside an Int64 without data loss). valueAsObject is T returns false when T is Int64 and valueAsObject is Int32.
Is it possible to safely cast valueAsObject to T without just trying it and catching any exceptions? I would like to avoid exceptions, though if that is the only way I can make it work.
public static void MyMethod<T>(IDictionary<String, Object> dictionary, String key, ref T destination)
{
if (!dictionary.ContainsKey(key))
return;
var valueAsObject = dictionary[key];
if (!(valueAsObject is T))
return;
destination = (T)valueAsObject;
}
Basically, I want the function to do nothing (return) if the thing found in the dictionary can not be safely stored in a type T variable (without data loss), otherwise it should cast it as necessary and store it.
This is not built-in at the language or runtime level. You can use Convert.ChangeType to perform the conversion. This method also performs lossy conversions so you probably have to build your own conversion logic (which will involve casts and be ugly).
You can't just do something like this?
public static void MyMethod<T>( IDictionary<string,object> dictionary , string key , ref T destination )
{
object value ;
bool found = dictionary.TryGetValue( key , out value ) ;
if (found && value is T)
{
destination = (T) value ;
}
return;
}
The is operator:
evaluates to true if the provided expression is non-null, and the provided object
can be cast to the provided type without causing an exception to be thrown.
.
.
.
Note that the is operator only considers reference conversions, boxing conversions,
and unboxing conversions. Other conversions, such as user-defined conversions, are not considered.
Basically, if the object directly inherits from the specified type, or implements the type (if T is an interface), is should return true and you should be able to down-cast the object to your type T.
If, however, you need to worry about user-defined conversion operators, you'll need to reflect over the object's type and over typeof(T) to see if a user-defined conversion operator is defined on either type that will convert the object to a compatible type. Getting that right is likely to be...tricky.
This is the final solution I went with. It came from a number of comments and other answers so I encourage reading the other answers/comments for additional details as to why this was necessary:
private static void GetFromDictionary<T>(IDictionary<String, Object> dictionary, String key, ref T outputLocation)
{
if (!dictionary.ContainsKey(key))
return;
var valueAsObject = dictionary[key];
if (!CanBeCastTo<T>(valueAsObject))
return;
outputLocation = (T)Convert.ChangeType(valueAsObject, typeof(T));
}
private static bool CanBeCastTo<T>(Object thingToCast)
{
if (thingToCast is T)
return true;
var tType = typeof(T);
if (tType == typeof(Int64) && thingToCast is Int32)
return true;
if (tType == typeof(Double) && thingToCast is Decimal)
return true;
return false;
}
In my case, I only needed to handle a certain subset of primitive types (Int32, Int64, String, Double, Decimal) and I was willing to accept lossy conversion of Decimal to Double.
The take away here is that the "is" operator will return false for primitives even if it can fit without loss (Int32 in Int64). Also, (T)valueAsObject will not do an explicit typecast. So if valueAsObject is of type Int32 and T is of type Int64 the above will throw an exception even though (Int64)valueAsObject is valid. This one bit me but luckily #usr suggested using Convert.ChangeType which handles the problem.
Optimizations could be made to the above code but I chose to leave it as is for readability.
I want to persist the currently selected value of a combo box and restore it at a later time. To manage the value in the combo box I have an enumerated type with description attributes. The description attribute becomes (one of) the combo box string values at runtime and the enumerated constant associated with it is used internally for programming purposes. I got this technique from the following Stack Overflow post:
c#:How to use enum for storing string constants?
That post contained a link in one of the comments to this blog post:
http://weblogs.asp.net/grantbarrington/archive/2009/01/19/enumhelper-getting-a-friendly-description-from-an-enum.aspx
The GetDescription() method that does the enum to string conversion magic is replicated here from that post, with the addition of the "this" keyword to the parameter list so I can use it as an extension method with enumerated types:
public static string GetDescription(this Enum en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
// Unable to find a description attribute for the enum. Just return the
// value of the ToString() method.
return en.ToString();
}
So I have one side of the equation fully fleshed out and it works quite well. Now I want to go the other way. I want to create a method that takes a string and returns the correct enumerated value by walking the description attributes for a particular enumerated type, and returning the enumerated value associated with the description attribute that matches the string. A hypothetical method declaration would be:
public static Enum GetEnumValue(string str){}
However an immediate problem with that declaration is that it does not return a specific enum type. I am not sure how to declare and cast things properly so that the correct enumerated type is returned. Is it possible to create this complementary method to the GetDescription() method and if so, how do I craft it so it works conveniently for any particular enum type? If I can do this I'll have a convenient solution for the common problem of converting between the strings used to persist control settings and then restoring them later, all supported by enums.
You are missing a piece of information, what Enum to look at.
Currently you are only passing in a string, but not the type of Enum.
The simplest way is to use a generic function
Note that contents of this are off the cuff and probably don't even compile.
public static TEnum GetEnumValue<TEnum>(string str)
where TEnum : struct //enum is not valid here, unfortunately
{
foreach (MemberInfo memInfo in typeof(TEnum).GetMembers())
{
object[] attrs = memInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
if (((DescriptionAttribute)attrs[0]).Description == str)
{
return (TEnum)(object)Enum.Parse(typeof(TEnum),memInfo.Name);
}
}
}
// Unable to find a description attribute for the enum.
return (TEnum)(object)Enum.Parse(typeof(TEnum),str);
}
Then you can use typeof(TEnum) to get the type object for the requested enumeration and do your logic.
Finally you can cast back to TEnum before returning, saving yourself the work on the calling side.
EDIT:
Added a rough example, untested.
What I want to do is something like this:
switch( myObject.GetType().GetProperty( "id") )
{
case ??:
// when Nullable<Int32>, do this
case ??:
// when string, do this
case ??:
// when Nullable<bool>, do this
What path under object.GetType() would have the string name of the datatype that I could compare using a case statement? I need to know the type so I can have one of many Convert.ToInt32( string ) that will set the value of myObject using Reflection.
I've been using the following type of code to check if the type is nullable and to get the actual type:
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return Nullable.GetUnderlyingType(type);
}
If the type is e.g. Nullable this code returns the int part (underlying type). If you just need to convert object into specific type you could use System.Convert.ChangeType method.
The question is very confusing. Is "myObject" the object that might be a nullable int? Or is the property "id" possibly of type nullable int?
If the former, your question cannot be answered because it presupposes a falsehood. There is no such thing as a boxed nullable int. I note that all of the answers which propose if (myobject.GetType() == typeof(int?)) are therefore incorrect; the condition will never be true.
When you convert a nullable int to object, either it becomes a null reference (if the nullable int had no value) or it becomes a boxed int. There is no way to determine if an object contains a nullable int because an object never contains a nullable int.
If the latter, compare the property type to typeof(int?). You cannot use a switch; only constants may be used for switch cases and types are not constants.
All that said, this is a bad code smell. Why are you using reflection in the first place?
Update: Looks like C# 7 will support switching on Types as the asker of this question was trying to do. It's a little different though so watch out for syntax landmines.
You don't need a string name to compare it:
if (myObject.GetType().GetProperty("id").PropertyType == typeof(Nullable<Int32>))
// when Nullable<Int32>, do this
else if (myObject.GetType().GetProperty("id").PropertyType == typeof(string))
// when string, do this
else if (myObject.GetType().GetProperty("id").PropertyType == typeof(Nullable<bool>))
// when Nullable<bool>, do this
In .net, instances of value types are just collections of bits, with no associated type information. For every value type other than Nullable<T>, however, the system also auto-generates a corresponding class type which derives from System.ValueType. A widening conversion exists from the value type to the auto-generated class type, and a narrowing conversion from the auto-generated class type to the value type. In the case of Nullable<T>, there is no corresponding auto-generated class type with conversions to/from the value type; instead, widening conversions exist in both directions between Nullable<T> and the class type associated with T.
As far as I can tell, this weird behavior was implemented to allow comparisons between null and an empty Nullable<T> to return true.
As #Cody Gray said if statements would probably be the best way
var t = myObject.GetType();
if (t == typeof(Nullable<int>))
{ }
else if (t == typeof(string))
{}
else if (t==typeof(Nullable<bool>))
{}
I was doing some testing with nullable types, and it didn't work quite as I expected:
int? testInt = 0;
Type nullableType = typeof(int?);
Assert.AreEqual(nullableType, testInt.GetType()); // not the same type
This doesn't work either:
DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable)); //FAIL
DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable<>)); //STILL FAIL
My question is why does testInt.GetType() return int, and typeof(int?) return the true nullable type?
According to the MSDN :
Calling GetType on a Nullable type
causes a boxing operation to be
performed when the type is implicitly
converted to Object. Therefore GetType
always returns a Type object that
represents the underlying type, not
the Nullable type.
When you box a nullable object, only the underlying type is boxed.
Again, from MSDN :
Boxing a non-null nullable value type
boxes the value type itself, not the
System.Nullable that wraps the value
type.
Further to Romain's correct answer, if you want to compare the "real" types (ie, without implicitly converting any nullable type to its underlying type) then you can create an extension method like so:
public static class MyExtensionMethods
{
public static Type GetRealType<T>(this T source)
{
return typeof(T);
}
}
And then try the following tests:
int? a = 0;
Console.WriteLine(a.GetRealType() == typeof(int?)); // True
Console.WriteLine(a.GetRealType() == typeof(int)); // False
int b = 0;
Console.WriteLine(b.GetRealType() == typeof(int)); // True
Console.WriteLine(b.GetRealType() == typeof(int?)); // False
DateTime? c = DateTime.Now;
Console.WriteLine(c.GetRealType() == typeof(DateTime?)); // True
Console.WriteLine(c.GetRealType() == typeof(DateTime)); // False
DateTime d = DateTime.Now;
Console.WriteLine(d.GetRealType() == typeof(DateTime)); // True
Console.WriteLine(d.GetRealType() == typeof(DateTime?)); // False
EDIT...
For completeness -- and prompted by SLaks's comments below -- here's an alternative version that only uses the compile-time type when source is either null or Nullable<>; otherwise it uses GetType and returns the runtime type:
public static class MyExtensionMethods
{
public static Type GetRealType<T>(this T source)
{
Type t = typeof(T);
if ((source == null) || (Nullable.GetUnderlyingType(t) != null))
return t;
return source.GetType();
}
}
Although C# pretends that value-type storage locations hold instances of types derived from System.ValueType, which in turn derives from System.Object, that isn't really true. Each type derived from System.ValueType actually represents two very different kinds of things:
A collection of bytes which (for primitive types) represents the data directly, or (for non-primitive structure types) holds the contents of all fields, public and private, but does not hold any type information.
A standalone heap object, which contains an object header in addition to the above, whose type is derived from `System.ValueType`.
Storage locations of a value type hold the first; heap objects of a value type hold the second.
For various reasons, Microsoft decided that Nullable<T> should only support the first usage. If one attempts to pass a storage location of type Nullable<T> to code which expects a reference to a heap object, the system will convert the item to a T if HasValue is true, or else simply pass a null reference if HasValue is false. While there are ways to create a heap object of type Nullable<T>, the normal methods of converting a value-type storage location to a heap object will never generate one.
Note also that calling GetType() on a value storage location won't actually evaluate the type of the storage location, but will instead convert the contents of that storage location to a heap object and then return the type of the resulting object. Because storage locations of type Nullable<T> get converted either to object instances of T or to null, nothing in an object instance will say whether the storage location from which it came was a Nullable<T>.
A simple way to check that is using the "is" operator:
(i is Nullable<int>) || (i is Nullable<long>) || (i is Nullable<float>) || (i is Nullable<short>)
I figured out ti reading these two MSDN pages:
http://msdn.microsoft.com/en-us/library/ms366789(v=vs.90).aspx
http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx
Cheers!