如何在C#中检查运行时类型的可分配性?

时间:2010-09-01 21:48:36

标签: c# types

Type类有一个几乎可以工作的方法IsAssignableFrom()。不幸的是,只有当两种类型相同或第一种类型在第二种类型的层次结构中时,它才会返回true。它表示十进制不能从int赋值,但是我想要一个方法来指示小数可以从int分配,但是int并不总是可以从小数分配。编译器知道这一点,但我需要在运行时解决这个问题。

这是扩展方法的测试。

    [Test]
    public void DecimalsShouldReallyBeAssignableFromInts()
    {
        Assert.IsTrue(typeof(decimal).IsReallyAssignableFrom(typeof(int)));
        Assert.IsFalse(typeof(int).IsReallyAssignableFrom(typeof(decimal)));
    }

有没有办法实现IsRessAssignableFrom(),它可以像IsAssignableFrom()一样运行,但也传递上面的测试用例?

谢谢!

编辑:

这基本上就是它的使用方式。这个例子不能为我编译,所以我必须将Number设置为0(而不是0.0M)。

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)]
    public class MyAttribute : Attribute
    {
        public object Default { get; set; }
    }

    public class MyClass
    {
        public MyClass([MyAttribute(Default= 0.0M)] decimal number)
        {
            Console.WriteLine(number);
        }
    }

我收到此错误:错误4属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式

6 个答案:

答案 0 :(得分:14)

实际上有三种方式,在您正在寻找的意义上,某种类型可以“分配”给另一种类型。

  • 类层次结构,接口实现,协方差和逆变。这是.IsAssignableFrom已经检查的内容。 (这还包括允许的装箱操作,例如intobjectDateTimeValueType。)

  • 用户定义的隐式转化。这是所有其他答案所指的内容。您可以通过Reflection检索这些内容,例如从intdecimal的隐式转换是一个静态方法,如下所示:

    System.Decimal op_Implicit(Int32)
    

    您只需要检查两种相关类型(在这种情况下,Int32Decimal);如果转换不在那些中,则它不存在。

  • C# language specification中定义的内置隐式转化。很遗憾,Reflection并未显示这些内容。您必须在规范中找到它们并手动将可分配性规则复制到您的代码中。这包括数字转换,例如intlong以及floatdouble,指针转换,可空转换(intint?)和{{3} }。

此外,用户定义的隐式转换可以使用内置隐式转换进行链接。例如,如果用户定义的隐式转换从int存在到某种类型T,那么它也会转换为从shortT的转换。同样,Tshort也会加倍Tint

答案 1 :(得分:2)

这个几乎有效...它正在使用Linq表达式:

public static bool IsReallyAssignableFrom(this Type type, Type otherType)
{
    if (type.IsAssignableFrom(otherType))
        return true;

    try
    {
        var v = Expression.Variable(otherType);
        var expr = Expression.Convert(v, type);
        return expr.Method == null || expr.Method.Name == "op_Implicit";
    }
    catch(InvalidOperationException ex)
    {
        return false;
    }
}

唯一不起作用的情况是基本类型的内置转换:它错误地返回true以进行明确的转换(例如intshort)。我想你可以手动处理这些情况,因为它们的数量有限(而且相当小)。

我真的不想捕获异常以检测无效转换,但我没有看到任何其他简单方法来执行此操作...

答案 2 :(得分:0)

为了找出是否可以将一种类型分配给另一种类型,您必须寻找从一种类型到另一种类型的隐式转换。你可以用反射来做到这一点。

正如Timwi所说,您还必须了解一些内置规则,但这些规则可以是硬编码的。

答案 3 :(得分:0)

你在寻找的是从一种类型到另一种类型的隐式演员。我认为这可以通过反射来实现,虽然它可能很棘手,因为隐式转换应该定义为操作符重载,这是一个静态方法,我认为它可以在任何类中定义,而不仅仅是可以隐式转换的类。

答案 4 :(得分:0)

Timwi的回答非常完整,但我觉得有一种甚至更简单的方式可以让你获得相同的语义(检查“真正的”可分配性),而不是实际定义自己这是什么。

您可以尝试有问题​​的作业并寻找InvalidCastException(我知道这很明显)。通过这种方式,您可以避免麻烦检查Timwi提到的可转让性的三种可能含义。这是使用xUnit的示例:

[Fact]
public void DecimalsShouldReallyBeAssignableFromInts()
{
    var d = default(decimal);
    var i = default(i);

    Assert.Throws<InvalidCastException)( () => (int)d);
    Assert.DoesNotThrow( () => (decimal)i);
}

答案 5 :(得分:-1)

实际上恰好是decimal类型不能“分配”到int类型,反之亦然。涉及装箱/拆箱时会出现问题。

以下面的例子为例:

int p = 0;
decimal d = 0m;
object o = d;
object x = p;

// ok
int a = (int)d;

// invalid cast exception
int i = (int)o;

// invalid cast exception
decimal y = (decimal)p;

// compile error
int j = d;

此代码看起来应该可以工作,但是从对象转换的类型会产生无效的强制转换异常,最后一行会生成编译时错误。

分配给a的原因是因为decimal类对类型转换运算符有明确的覆盖int。从decimalint不存在隐式类型转换运算符。

编辑:反向中甚至不存在隐式运算符。 Int32实现IConvertible,这就是它转换为十进制的方式 结束修改

换句话说,类型不是可分配,而是可转换

您可以扫描程序集以查看显式类型转换操作符和IConvertible接口,但是我得到的印象是不能为您提供服务以及您将遇到的特定几种情况的编程。

祝你好运!