在c#中将一个动态转换为另一个的类型

时间:2014-10-01 16:43:09

标签: c# dynamic casting

我试图编写一个通用函数来比较反射的预期结果(但是在用户的配置中而不是在设计时提供的期望)与任意属性的实际结果相比较。

我遇到了一个问题,即预期的类型默认情况下并不总是反映返回的类型 - 例如我的反射结果(在动态中)可能是一个int,其中预期的结果是一个枚举成员(从int继承)。

我想,因此要做到以下几点:

if ((dCurrentValue as typeof(this.CheckValue)) != this.CheckValue) { oOut = false; }
但是,这似乎不起作用。从网络上的笨拙中,我设法发现System.Activator或Convert.ChangeType()可能是我的朋友。然而,到目前为止,他们并没有像我期望的那样工作 - 例如:

dCurrentValue = Convert.ChangeType(dCurrentValue, this.CheckValue.GetType());

抛出异常(对于提醒我这个问题的那对)Invalid cast from 'System.Int32' to 'Microsoft.Office.Core.MsoTriState' - 我知道这是错的,因为:

(int)Microsoft.Office.Core.MsoTriState.msoTrue == -1                                    // true
((Microsoft.Office.Core.MsoTriState)(-1)) == Microsoft.Office.Core.MsoTriState.msoTrue  // true

请注意,虽然我可以设置一个垫片来解决MsoTriState(即检查this.CheckValue的类型,并在适用的情况下进行显式转换),但我宁愿以一种能够为其工作的方式执行此操作未知的枚举条目。

编辑:感谢下面的评论,我在测试表格之前添加了测试:

if (((Type) this.CheckValue.GetType()).IsEnum)
{
    dCurrentValue = Enum.Parse(this.CheckValue.GetType(), dCurrentValue.ToString());
}

修复了我的直接问题。我的猜测结合Convert.ChangeType()(正如我所提到的,似乎并不喜欢将Enums转换为Ints)将涵盖大多数情况。

1 个答案:

答案 0 :(得分:2)

公共语言运行时类型(BooleanSByteByte ...)实现IConvertibleConvert仅适用于实现此接口的类型。基类型之间的转换不是问题。 Enum不属于公共语言运行时类型,但它实现了IConvertible。这意味着您可以使用Enum轻松地从Convert.ChangeType转换为基本类型,但您不能简单地向后执行 - 这两种类型之间没有 bridge 。这就是你抓住Invalid cast from 'System.Int32' to 'Microsoft.Office.Core.MsoTriState'的原因。 IConvertible包含方法(GetTypeCode),有助于获取有关Enum基本类型的信息。我写了一些代码来解决你的问题。

解决方案

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)); 
    }
}