繁体   English   中英

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

[英]How to check assignability of types at runtime in C#?

Type类有一个几乎有效的方法IsAssignableFrom() 不幸的是,它仅在两种类型相同或第一种类型在第二种类型的层次结构中时才返回 true。 它说decimal不能从int分配,但我想要一种方法可以表明decimal s 可以从int分配,但int s 并不总是可以从decimal s 分配。 编译器知道这一点,但我需要在运行时弄清楚这一点。

这是一个扩展方法的测试。

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

有没有办法实现IsReallyAssignableFrom() ,它可以像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 表达式或数组创建表达式

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

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

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

     System.Decimal op_Implicit(Int32)

    您只需要检查两个相关类型(在本例中为Int32Decimal ); 如果转换不在那些中,则它不存在。

  • C# 语言规范中定义的内置隐式转换。 不幸的是,Reflection 没有显示这些。 您必须在规范中找到它们并手动将可分配性规则复制到您的代码中。 这包括数字转换,例如intlong以及floatdouble 、指针转换、可为空的转换( intint? )和提升转换

此外,用户定义的隐式转换可以与内置的隐式转换链接。 例如,如果存在从int到某种类型T的用户定义的隐式转换,那么它也兼作从shortT的转换。 类似地, T to shortT to int

这个几乎有效......它使用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;
    }
}

唯一不起作用的情况是原始类型的内置转换:对于应该是显式的转换(例如intshort ),它错误地返回true 我想您可以手动处理这些情况,因为它们的数量有限(而且相当少)。

我真的不喜欢捕捉异常来检测无效转换,但我没有看到任何其他简单的方法来做到这一点......

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

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

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

您正在寻找的是是否存在从一种类型到另一种类型的隐式转换。 我认为这可以通过反射来实现,尽管它可能很棘手,因为隐式强制转换应该定义为运算符重载,它是一种静态方法,我认为它可以在任何类中定义,而不仅仅是可以隐式转换的类。

实际上, 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;

这段代码看起来应该可以工作,但是从 object 类型转换产生了一个无效的转换异常,最后一行产生了一个编译时错误。

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

编辑:甚至不存在反向隐式运算符。 Int32 实现了 IConvertible,这就是它如何转换为十进制End Edit

换句话说,类型不是assignable ,而是convertible

您可以扫描程序集以查找显式类型转换运算符和 IConvertible 接口,但我的印象是,对于您知道会遇到的特定少数情况,这不会为您提供服务以及编程。

祝你好运!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM