首页 > 解决方案 > 使用 .net 核心在应用程序加载的程序集中查找所有特定类型,并使用反射

问题描述

注意:虽然示例代码使用 F#,但我并不是专门寻找 F# 答案,而是寻找我需要做的 .net 调用,因此 C# 或 F# 中的答案很好。

我有一个系统,其中有用于聊天机器人的命令,分散在代码的不同部分。我使用以下代码通过反射找到并注册它们:

member private this.RegisterCommands() =
    let t = typeof<IBotCommands>
    t.Assembly.GetTypes()
    |> Seq.filter (fun x -> x.GetInterface(t.Name) <> null)
    |> Seq.iter (fun x ->
        (
            let i = (Activator.CreateInstance(x)) :?> IBotCommands
            botCommands.[i.Name()] <- i
            i.Initialize(this)
        )
    )

它在 F# 中,但如果您更熟悉 C#,我认为方法调用会放弃它的作用。

所以,我做 Assembly.GetTypes,保留 GetInterface 具有我正在寻找的接口名称的那些,然后我实例化它们。

这很好用,但只能在它自己的程序集中。我的问题是如何将其扩展到应用程序加载的所有程序集(但系统程序集不会浪费时间)?

标签: c#.net-corereflectionf#

解决方案


让您的用户程序集定义一个程序集级别属性 - 您可以检查它,以决定进一步扫描库。这比找出将哪些程序集列入黑名单更可靠,例如,您可能不想扫描Newtonsoft.Json.

   Assembly
       .GetEntryAssembly()   
       .GetReferencedAssemblies()    
       |> Seq.map    (fun assm -> Assembly.Load assm.FullName)
       |> Seq.append (Seq.singleton (Assembly.GetEntryAssembly()))
       |> Seq.filter (fun assm -> assm.IsDefined(useAttrib) )
       |> Seq.collect(fun assm -> assm.GetTypes())       
       |> Seq.filter (fun x -> interfaceType.IsAssignableFrom(x))
       |> Seq.iter (printfn "%A")

如果您的要求将来会变得更加复杂,那么可能值得一看 MEF。

let catalog = new DirectoryCatalog("./")
let container = new CompositionContainer(catalog)
container.ComposeParts()

let instances = container.GetExports<IBotCommands, IBotCommandMetadata>()

推荐阅读