Reputation: 1167
I'm trying to write a generic function that compares expected results from reflection (but where the expectation is provided in configuration by users rather than at design time) with the actual results for arbitrary properties.
I'm running into an issue where the expected type doesn't always reflect the returned type by default - e.g. my reflection result (in a dynamic) may be an int, where the expected result is an enum member (inheriting from int).
I'd like, therefore to do the following:
if ((dCurrentValue as typeof(this.CheckValue)) != this.CheckValue) { oOut = false; }
however, this doesn't seem to work. From fumbling around the web, I've managed to find that either System.Activator or Convert.ChangeType() may be my friends. However, so far they're not working as I'd expect - e.g.:
dCurrentValue = Convert.ChangeType(dCurrentValue, this.CheckValue.GetType());
throws an exception (for the pair that alerted me to the issue) that Invalid cast from 'System.Int32' to 'Microsoft.Office.Core.MsoTriState'
- which I know to be wrong, since:
(int)Microsoft.Office.Core.MsoTriState.msoTrue == -1 // true
((Microsoft.Office.Core.MsoTriState)(-1)) == Microsoft.Office.Core.MsoTriState.msoTrue // true
NB that whilst I could put a shim in to solve for MsoTriState (i.e. check type of this.CheckValue, and explicit cast if applicable), I'd rather do this in a way that'll work for unknown enum entries.
EDIT: Thanks to the comments below, I've added a test before my tests of the form:
if (((Type) this.CheckValue.GetType()).IsEnum)
{
dCurrentValue = Enum.Parse(this.CheckValue.GetType(), dCurrentValue.ToString());
}
which fixes my immediate issue. My guess is this combined with Convert.ChangeType()
(which as I've mentioned, doesn't seem to like converting Enums to Ints) will cover most situations.
Upvotes: 3
Views: 341
Reputation: 4202
The common language runtime types (Boolean
, SByte
, Byte
...) implement IConvertible. Convert
works only with types which implement this interface. Сonversion between the base types is not a problem. Enum doesn't belong to the common language runtime types, but it implements IConvertible
. It means that you can easily convert from Enum
to the base type using Convert.ChangeType
, but you cannot simply do it in backward direction – there is no bridge between these two types. This is the reason why you caught Invalid cast from 'System.Int32' to 'Microsoft.Office.Core.MsoTriState'
. IConvertible
contains method (GetTypeCode
) which helps to gain information about the base type of Enum
. I wrote a bit of code in order to solve your problem.
public static class Comparer
{
public static IConvertible CastToConvertible<T>(T value)
{
if (value is IConvertible)
{
return (IConvertible)Convert.ChangeType(value, ((IConvertible)value).GetTypeCode());
}
// this type is not supported
throw new ArgumentException("Unknown type: " + value.GetType());
}
public static bool Equals<T1, T2>(T1 first, T2 second)
{
try
{
IConvertible firstConveted = CastToConvertible(first);
IConvertible secondConverted = CastToConvertible(second);
// standard Equals cannot compare two different types,
// so here the second value is
// converted to the type of the first value
var secondChangedType = (IConvertible)Convert.ChangeType(
secondConverted, firstConveted.GetTypeCode());
return firstConveted.Equals(secondChangedType);
}
catch (Exception)
{
// an exception might be caught in two cases:
// 1. One of the values cannot be converted
// to IConvertible interface.
// 2. The second value cannot be converted
// to the type of the first value.
return false;
}
}
}
[TestClass]
public class ComparerTests
{
public enum ByteEnum : byte
{
One = 1, Two = 2
}
public enum IntEnum
{
One = 1, Two = 2, MegaLarge = 100500
}
[TestMethod]
public void EqualsTest()
{
Assert.IsTrue(Comparer.Equals(2, 2));
Assert.IsFalse(Comparer.Equals(1,2));
Assert.IsTrue(Comparer.Equals(1, IntEnum.One));
Assert.IsFalse(Comparer.Equals(1, IntEnum.Two));
Assert.IsTrue(Comparer.Equals(ByteEnum.One, IntEnum.One));
Assert.IsFalse(Comparer.Equals(ByteEnum.One, IntEnum.Two));
Assert.IsFalse(Comparer.Equals(ByteEnum.One, IntEnum.MegaLarge));
}
}
Upvotes: 2