如何查看一个类型是否实现了一个接口?

6

我需要知道一个Type是否实现了一个接口。

 Dim asmRule As System.Reflection.Assembly = System.Reflection.Assembly.LoadFrom(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Rule.dll"))

 For Each typeAsm As System.Type In asmRule.GetTypes
     If TypeOf typeAsm Is Rule.IRule Then
       'that does always return false even though they implement IRule'
     End If
 Next

感谢大家。现在我知道typeof为什么不起作用了。类型自然而然地没有实现IRule。从你们的答案中,我筛选出了两个选项:
  1. GetType(Rule.IRule).IsAssignableFrom(typeAsm)
  2. typeAsm.GetInterface(GetType(Rule.IRule).FullName) IsNot Nothing
根据性能,哪个选项更好?
更新:我已经发现使用以下方法可能更好:
Not typeAsm.GetInterface(GetType(Rule.IRule).FullName) Is Nothing

替代

GetType(Rule.IRule).IsAssignableFrom(typeAsm)

因为接口IRule本身可以分配给IRule,如果尝试创建实例,则会引发MissingMethodExcpetion:

ruleObj = CType(Activator.CreateInstance(typeAsm, True), Rule.IRule)

更新2: 感谢Ben Voigt的建议。他让我相信,结合IsAbstract使用IsAssignableFrom可能是检查给定类型是否实现了一个接口且不是接口本身(如果您尝试创建一个实例,会抛出MissingMethodException)的最佳方法。

If GetType(Rule.IRule).IsAssignableFrom(typeAsm) AndAlso Not typeAsm.IsAbstract Then

2
C#的is关键字在这里无法使用。 - SLaks
@Justin Niessner:是的,这个dll中的大多数类都实现了IRule接口,但是TypeOf typeAsm Is Rule.IRule总是返回false。 - Tim Schmelter
那是因为System.Type类没有实现IRule接口。请看我的回答。 - SLaks
5个回答

13

TypeOf ... Is 继承时能够很好地工作:

Imports System

Public Class Test

    Public Shared Sub Main()
        Dim o As Object = 5

        If TypeOf o Is IFormattable Then
            Console.WriteLine("Yes")
        End If
    End Sub    

End Class

这在 C# 中的作用与 "is" 相同。

然而,在你的例子中,它试图查看 System.Type(或实际使用的任何子类)是否实现了 Rule.IRule。这不会起作用......因为你不关心 System.Type 是否实现了该接口,你关心的是 typeAsm 变量所引用的 System.Type 实例是否实现了 IRule.Rule

换句话说,你的代码在 C# 中也不会更有效。当涉及到 System.Type 的实例时,你需要使用Type.IsAssignableFrom

If GetType(Rule.IRule).IsAssignableFrom(typeAsm) Then

在编程中,哪种选择更好(请参见我的更新):使用IsAssignableFrom还是myType.GetInterface("Rule.IRule") IsNot Nothing? 谢谢。 - Tim Schmelter
1
@Tim:就我个人而言,我更喜欢使用IsAssignableFrom,因为它已经在询问一个布尔问题了,而且在其他情况下也适用。不过不必纠结 :) - Jon Skeet
我发现在我的情况下,使用以下代码可能更好:Not typeAsm.GetInterface(GetType(Rule.IRule).FullName) Is Nothing。因为IRule接口本身可以从IRule派生。如果我尝试使用ruleObj = CType(Activator.CreateInstance(typeAsm, True), Rule.IRule)创建实例,就会导致Missing MethodException。 - Tim Schmelter
2
@Tim:GetInterface也不是更好的选择,因为抽象类可以实现接口。你需要检查IsAbstract。尽可能使用类型cookie而不是名称会更好。 - Ben Voigt
@Ben:感谢你的IsAbstract提示 :) - Tim Schmelter

5

我想在发布之前刷新一下,结果正确答案就出现了。+1 - Randolpho
这个答案是正确的,但不会实现他想要做的事情。 - SLaks
哇,我应该更好地阅读问题。我想我犯了那种匆忙回复综合症的错误,这是我在这个网站上感到非常恼人的。 - Steve Whitfield
Steve_:我修改了问题,不再那么具有误导性。无论是对于哪个问题,你的答案都是不正确的。 - Gabe

3

感谢您提供这个有效的答案。对我来说,这是最好的方法,因为它避免了创建 IRule 实例本身的问题。这就是 IsAssignableFrom 的问题所在。但最终我还是选择Jon的答案,因为他的回答讲解得最好。 - Tim Schmelter

3

你使用了错误的关键字。

C# 的 is 关键字和 VB.Net 的 TypeOf ... Is ... 会检查实例是否是实现该接口的类型。
因此,你的代码将检查 typeAsm 变量是否是 System.Type 实例,并且实现了 IRule 接口。
由于 System.Type 类不实现 IRule 接口,它将始终返回 False

实际上,你要检查的是由 System.Type 实例所表示的类型是否实现了 IRule
为了做到这一点,你需要使用 System.Type 类中的方法。

例如:

 If typeAsm.GetInterface(GetType(Rule.IRule)) IsNot Nothing Then

typeAsm.GetInterface(GetType(Rule.IRule).FullName) IsNot Nothing 是正确的方式,因为 GetInterface 需要作为字符串的名称。但还是谢谢。 - Tim Schmelter
GetInterface 不接受 System.Type,所以你的代码片段无法工作。使用类型名称作为字符串有些脆弱和低效。我真的认为调用 IsAssignableFrom 是最优的方式。 - Ben Voigt

2

@Jon:我觉得IRule看起来像是一个接口。 - Ben Voigt
@Ben 然后你可以使用 Type/GetInterface()。 - rerun
感谢您的回答并澄清IsAssignableFrom与IsAbstract结合可能是最好的方法(请参见Update2)。 - Tim Schmelter
@Tim:我认为你没有理解我的“IsAbstract”建议的重点。你想要排除不仅是IRule本身,还包括从IRule派生的任何接口和实现IRule的抽象类,因为对于这些情况,使用Activator.CreateInstance也会失败。请注意,CreateInstance也可能因其他原因而失败(例如,您不能在泛型类型模式上使用它--使用ContainsGenericParameters进行测试,或者如果没有默认构造函数)。 - Ben Voigt
@Tim:是的,你说得对。顺便说一句,如果你关心效率,可以交换AndAlso的位置,把“IsAbstract”测试放在前面,这样对于接口和抽象类,甚至不会尝试进行“IsAssignableFrom”检查。 - Ben Voigt
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接