【问题标题】:Is it possible to execute static code using Dart?是否可以使用 Dart 执行静态代码?
【发布时间】:2014-01-25 14:29:22
【问题描述】:

Java 和 Javascript 都允许以不同的方式执行静态代码。 Java 允许您在类的主体中拥有静态代码,而 JS 允许您在类定义之外执行静态代码。例子:

Java:

public class MyClass {
    private static Map<String,String> someMap = new HashMap<String,String();
    static {
        someMap.put("key1","value");
        someMap.put("key2","value");
        SomeOtherClass.someOtherStaticMethod();
        System.out.println(someMap);
    }
}

JS(基本上是类外的任何 JS 代码):

var myint = 5;
callSomeMethod();
$(document).ready(function () {
    $("#hiddenelement").hide();
});

但是,Dart 似乎支持这两种方式中的任何一种。支持声明全局变量和方法,但不支持像 JS 中那样调用方法和执行代码。这只能在main() 方法中完成。另外,类中的静态代码也是不允许的。

我知道 Dart 有其他方法可以像我的第一个示例一样静态填充 Map,但是我可以想到另一种情况需要这样做。

让我们考虑以下CarRegistry 实现,它允许您将汽车模型的字符串映射到相应类的实例。 F.e.当您从 JSON 数据中获取汽车模型时:

class CarRegistry {
    static Map<String, Function> _factoryMethods = new HashMap<String, Function>();

    static void registerFactory(String key, Car factory()) {
        _factoryMethods[key] = factory;
    }

    static Car createInstance(String key) {
        Function factory = _factoryMethods[key];
        if(factory != null) {
            return factory();
        }
        throw new Exception("Key not found: $key");
    }
}

class TeslaModelS extends Car {

}

class TeslaModelX extends Car {

}

为了能够调用CarRegistry.createInstance("teslamodelx");,必须首先注册该类。在 Java 中,这可以通过在每个 Car 类中添加以下行来完成:static { CarRegistry.registerFactory("teslamodelx" , () =&gt; new TeslaModelX()); }。您不希望将所有汽车硬编码到注册表中,因为它会失去它作为注册表的功能,并且会增加耦合度。您希望能够通过仅添加一个新文件来添加新车。在 JS 中,您可以在类构造之外调用 CarRegistry.registerFactory("teslamodelx" , () =&gt; new TeslaModelX()); 行。

如何在 Dart 中完成类似的事情?

即使您允许编辑多个文件以添加新车,如果您编写的库没有main() 方法也是不可能的。唯一的选择是在第一次调用 Registry.createInstance() 方法时填充地图,但它不再是注册表,而是包含硬编码汽车列表的类。

编辑:对我在此处所做的最后声明的一个小补充:仅当注册表驻留在我自己的库中时,才可以选择在 createInstance() 方法中填充这种注册表。如果,f.e.我想将自己的类注册到由我导入的不同库提供的注册表中,这不再是一个选项。

【问题讨论】:

    标签: static dart factory


    【解决方案1】:

    为什么要对静态大惊小怪? 您可以创建一个 getter 来检查初始化是否已经完成 (_factoryMethods != null),如果没有这样做并返回地图。

    据我了解,这完全是关于应该在什么时间执行这段代码。

    我上面展示的方法是延迟初始化。
    我认为这通常是我猜的首选方式。
    如果您想在加载库时进行初始化
    我不知道从main() 调用库的init() 方法并添加初始化代码这个库init() 方法的另一种方法。

    这里是关于这个话题的讨论executing code at library initialization time

    【讨论】:

    • 就像 dart-misc 小组中的人提到的那样,我希望我的库可以在不需要用户调用初始化方法的情况下使用。惰性初始化方法在本例中可能有效,但是当我尝试将自己的类注册到另一个库提供的注册表中时,这是不可能的。
    • 不可能。 Dart 被刻意设计为在函数体之外没有副作用。在main 之前没有代码执行,没有代码自动执行而不被主函数直接或间接调用。
    【解决方案2】:

    我在尝试驱动类似主题的库时遇到了同样的问题。

    我最初尝试使用dart:mirrors 来遍历库中的类并确定它们是否被这样的注释标记(使用您自己的代码作为示例的一部分):

    @car('telsamodelx')
    class TelsaModelX extends Car {
    }
    

    如果是这样,它们会自动填充到注册表中。不过,性能不是很好,我不确定它是否会扩展。

    我最终采取了一种更麻烦的方法:

    // Inside of CarRegistry.dart
    class CarRegister {
      static bool _registeredAll = false;
      static Car create() {
        if (!_registeredAll) { _registerAll()); }
        /* ... */
      }
    }
    
    // Inside of the same library, telsa_model_x.dart
    class TelsaModelX extends Car {}
    
    // Inside of the same library, global namespace:
    // This method registers all "default" vehicles in the vehicle registery.
    _registerAll() {
      register('telsamodelx', () => new TelsaModelX());
    }
    
    // Inside of the same library, global namespace:
    register(carName, carFxn) { /* ... */ }
    

    在图书馆外,消费者必须在某个地方致电register(); 注册他们的车辆。

    这是不必要的重复,不幸的是,将注册与类分开,难以跟踪,但它要么是繁琐的代码,要么是使用dart:mirrors.的性能损失

    YMMV,但随着可注册项目数量的增加,我开始再次关注dart:mirrors 方法。

    【讨论】:

    • 您能否链接到您在其中查找带注释的类的代码?
    • 我正在查看我的源代码,但它不存在——我想我可能已经丢弃了代码分支而没有先将它推送到任何地方。悬崖笔记版本是:使用currentMirrorSystem from dart:mirrors 遍历每个LibraryMirror,并在每个LibraryMirror 中遍历每个类,在metadata 内部查找与您的注释匹配的ClassMirror 对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-07
    • 1970-01-01
    • 2010-10-24
    相关资源
    最近更新 更多