如何将模块名称作为字符串调用模块的方法?我知道怎么用申请。我怎样才能将模块的名称用作字符串?
答案 0 :(得分:2)
您必须将字符串转换为原子。所有Elixir模块都位于Elixir
命名空间中(基本上是为了防止名称冲突),因此表示Elixir模块的原子都以"Elixir."
开头:
iex(1)> defmodule M, do: def foo, do: IO.puts "BAR"
iex(2)> name = "M"
iex(3)> apply(String.to_atom("Elixir." <> name), :foo, [])
BAR
答案 1 :(得分:0)
Elixir 模块在使用和打印时省略其名称中的 Elixir.
前导部分。当 Elixir 和 Erlang 模块都被编译时,它们会生成一个 Beam 文件。需要这个前缀是为了避免 Elixir 和 Erlang 同名的名称冲突。
当您编写和阅读代码时,您不必考虑这一点,因为它是自动处理的。注意左右两边都是原子。需要引号来处理特殊符号,模块只是以大写字母开头的原子,作为语法糖,在这种情况下可以省略 :
。
iex(1)> :"Elixir.SomeModule" == SomeModule
true
iex(2)> :"Elixir.SomeModule" == Elixir.SomeModule
true
由于编译器会自动为您处理这些前缀,因此建议您在将字符串转换为模块名称时也让编译器处理这些前缀。
这可以通过使用 Module.concat/1
和 Module.safe_concat/1
来完成。它们之间的区别在于,第一个使用 binary_to_atom/2
,第二个使用 binary_to_existing_atom/2
将字符串转换为原子。原子不会被垃圾收集,因此将随机用户输入作为参数提供给 binary_to_atom/2
或其等效的 Elixir String.to_atom/1
是危险的,因为这可能导致 VM 崩溃。在此特定用例中,如果您尝试从不存在的模块调用函数,则会遇到错误。
知道这一点,Module.safe_concat/1
函数是最合适的,因为:
Elixir.
前缀。成功案例:
iex(1)> Module.safe_concat(["String"]).upcase("xyz")
"XYZ"
模块名称有拼写错误时报错:
iex(2)> Module.safe_concat(["Strign"]).upcase("xyz")
** (ArgumentError) argument error
:erlang.binary_to_existing_atom("Elixir.Strign", :utf8)
(elixir 1.12.0-dev) src/elixir_aliases.erl:126: :elixir_aliases.safe_concat/1