实例化一个 Raku class,并在构造函数中更新一个实例变量

Instantiate a Raku class, and update an instance variable in the constructor

我似乎很难理解如何在 Raku 中正确使用 classes。

我正在尝试创建一个 'Database' class,我将在整个 cro 应用程序中使用它。但是我似乎不明白如何在对象构造(BUILD?)时处理设置实例变量。

下面的 class 显示了我在调用它时遇到的问题的示例:

通话中:

my $db = Database.new(dsn => 'update me!');
say "what value is foo: " ~ $db.what-is-foo(); # prints "what value is foo: 0" but expecting 123

我的Class:

use DB::Pg;

class Database {
    has Str    $.dsn is required;
    has DB::Pg $!dbh = Nil;
    has Int    $!foo = 0;

    method new (:$dsn) {
        my $dbh = DB::Pg.new(conninfo => :$dsn);
        my $foo = 123;
        return self.bless(:$dsn, dbh => $dbh, foo => $foo);
    }

    method what-is-foo() {
        return $!foo;
    }
}

所以在我的 class 构造函数中,我想为数据库 dsn 传递一个命名的 arg。 在我打算连接的新方法中,将实例变量设置为连接句柄。

在这个例子中,我使用一个简单的整数 (foo) 作为测试。

到目前为止,我发现 the docs here 不包含此类模式的示例,除了 pehaps:

use DB::Pg;

class Database {
    has Str    $.dsn is required;
    has DB::Pg $!dbh = Nil;
    has Int    $!foo = 0;

    submethod BUILD(:$dsn) {
        $!dbh = DB::Pg.new(conninfo => :$dsn);
        $!foo = 123;
    }

    method what-is-foo() {
        return $!foo;
    }
}

但这给了我:

The attribute '$!dsn' is required, but you did not provide a value for it.

如有任何帮助,我们将不胜感激!

只有具有 public 访问器的属性(即 . twigil)会自动设置为 bless

你有两种方法来处理这个问题。您可以将它们设置为 TWEAKBUILD,或者您可以将属性 is built 添加到属性中以自动为您完成此操作。在这种情况下,属性 is built 可能是最简单的,只需说 has Int $!foo is built = 0。但值得扩展其他选项:

如果您包含 BUILD 方法,您将自行负责所有设置。这意味着 public 私有属性。但是你可以通过巧妙地命名参数来让你的生活更轻松:

method BUILD (:$!dsn, :$!dbh, :$!foo) { }

实际上就是这样。签名将传入值绑定到 $!dsn 等,这当然会为整个对象实例设置它们。当然,你也可以在这里做更奇葩的事情。无论如何,在 BUILD 之后,还会进行一些额外的检查。 (1)如果你不设置$!dsn,因为你有is required,就会报错。 (2) 如果你最终没有设置$!foo$!dbh,他们将收到他们的默认值。

使用 TWEAK,您将获得与使用 BUILD 相同的参数,但所有初始设置都已完成(构建或自动绑定到 public 属性,和所有默认值,并保证所需的值存在)。您只是有机会进行一些最后的调整。