约束类属性的推荐方法?

Ste*_*ieD 9 raku

我有一个带有字符串属性的类。该属性表示文件的路径。我想在构造对象之前确保该文件存在。将来,我可能还想对文件进行额外的检查,例如它的格式是否正确。

无论如何,如果该文件不存在,我想抛出一个描述性异常。

经过一番尝试和错误后,我想出了这个:

unit class Vim::Configurator;
sub file-check($file) {
    die (X::IO::DoesNotExist.new(:path($file), :trying('new'))) if !$file.IO.f.Bool;
    return True;
}

has Str:D $.file is required where file-check($_);
Run Code Online (Sandbox Code Playgroud)

但众所周知,有不止一种方法可以做到这一点。

另一种选择是将约束逻辑放入neworbuild方法中。这没问题,但这感觉很老套,我想我更喜欢将每个属性的逻辑与第一个示例中的属性一起拼写出来。

第三种选择:

has Str:D $.file is required where *.IO.f.Bool == True;
Run Code Online (Sandbox Code Playgroud)

这很好而且简洁,但是抛出的错误非常难以理解。

第四个选项是使用subset以下内容来约束属性:

subset Vim::Configurator::File where *.IO.f.Bool == True;

unit class Vim::Configurator;
has Vim::Configurator::File $.file is required;
Run Code Online (Sandbox Code Playgroud)

这里抛出的错误消息也不是最大的。另外,我觉得很奇怪。

我确信还有其他方法可以给这只猫剥皮,我想知道其他人在做什么,以及是否有比上述任何方法更好的方法。谢谢。

Mus*_*dın 8

第三种选择:
has Str:D $.file is required where *.IO.f.Bool == True;
这很简洁,但抛出的错误非常难以理解。

您可以在 where 子句中有一个块并在那里抛出:

class A {
    has Str:D $.file where { .IO.f orelse .throw }
}

A.new(file => "absent");
Run Code Online (Sandbox Code Playgroud)

这给出了类似的东西

Failed to find 'absolute/path/to/absent' while trying to do '.f'
  in block  at ...
  in block <unit> ...
Run Code Online (Sandbox Code Playgroud)

where子句中,$_是传递的文件字符串,即上面的“absent”。然后检查它作为文件是否存在;.IO.f如果找不到文件,则失败,因此对于orelse$_将是它导致的失败,然后是.thrown。

但是,如果传递的文件字符串确实存在,但它不是文件,而是目录,则.IO.f不会失败,而是返回 False!那么将orelse不会切换到,.throw因为 False 是一个已定义的值。在这种情况下,我们又回到了无用的消息。为此,我们可以先检查存在性,并单独处理文件性:

Failed to find 'absolute/path/to/absent' while trying to do '.f'
  in block  at ...
  in block <unit> ...
Run Code Online (Sandbox Code Playgroud)

进而

>>> A.new(file => "absent")
"absent" does not exist at all
  in block ...

>>> A.new(file => "existing_dir")
"existing_dir" is not a file
  in block ...
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案,我唯一要补充的是,您可能希望将其设为“子集”:“subset FileName of Str where { ... }”然后您可以定义“has FileName:D $.file” `。 (2认同)

Raw*_*ler 6

您可以覆盖默认TWEAKBUILD方法来测试文件是否存在,如果不存在,则die.

class File {
    has Str $.path is required;
    
    submethod BUILD(:$path) {
        die 'File $path does not exist!' unless $path.IO.e; # Check if the file corresponding to the path exists
        $!path := $path;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您想要更细粒度的控制,您还可以编写一个工厂来进行检查,而不是在类级别上进行检查。

进一步阅读: https: //docs.raku.org/language/objects#Object_construction