我有如下代码,我想对其进行改进:
// example type
class Stuff
{
public function __construct($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}
// generator function
function searchStuff()
{
yield new Stuff('Fou');
yield new Stuff('Barre');
yield new Stuff('Bazze');
}
// code that iterates over the results of the generator
$stuffIterator = searchStuff();
assert($stuffIterator instanceof Iterator);
foreach ($stuffIterator as $stuff) {
/** @var Stuff $stuff */
echo $stuff->getName() . PHP_EOL;
}
我要改进的是循环中的注释(第三行),我想将其完全删除。原因是
我朴素的方法是声明一个迭代器接口,该接口向通用Iterator
接口添加适当的类型注释:
interface StuffIterator extends Iterator
{
public function current(): Stuff;
}
这有一个缺点,我不能在函数上将其设置为“硬”注释,而只能将其设置为文档字符串注释,因为"Generators may only declare a return type of Generator, Iterator, Traversable, or iterable"
不好,因为它不被强制执行。此外,我的IDE不会选择类型,但这是另一个问题。
另一种方法是编写一个实际的迭代器类,该类包装从函数返回的Generator
。问题在于,该类也需要实例化,因此我必须调用$stuffGenerator = new StuffIterator(searchStuff());
或编写另一个包装函数来做到这一点,而这两者都不是必需的。尽管如此,愚蠢的IDE仍未收到类型提示(grrrr ...!)。
所以,这是我的问题:这种方法有哪些替代方案?我可以想象像C ++或Java泛型这样的东西,但是,a,我不能简单地重写有问题的应用程序。
更多说明:
答案 0 :(得分:2)
一个很好的问题。我想您问题的答案不会像您预期的那样出现。该解决方案可能不是很好,但是可行。首先,您无法定义Generator
以外的收益率返回类型,依此类推。您已经给出了答案。但是...
只需想象以下起点。
class Stuff
{
protected $name;
public function getName() : ?string
{
return $this->name;
}
public function setName(string $name) : Stuff
{
$this->name = $name;
return $this;
}
}
class StuffCollection extends \IteratorIterator
{
public function __construct(Stuff ...$items)
{
parent::__construct(
(function() use ($items) {
yield from $items;
})()
);
}
public function current() : Stuff
{
return parent::current();
}
}
我在这里做了什么?我们已经知道Stuff
类。它没有新内容。新事物是StuffCollection
类。由于是从IteratorIterator
类扩展而来的,因此我们可以覆盖IteratorIterator::current()
方法并为其提供类型提示。
$collection = new StuffCollection(
(new Stuff())->setName('One'),
(new Stuff())->setName('Two'),
(new Stuff())->setName('Three')
);
foreach ($collection as $item) {
var_dump(assert($item instance of Stuff));
echo sprintf(
'Class: %s. Calling getName method returns "%s" (%s)',
get_class($item),
$item->getName(),
gettype($item->getName())
) . "<br>";
}
输出应该是...
bool(true) Class: Stuff. Calling getName method returns "One" (string)
bool(true) Class: Stuff. Calling getName method returns "Two" (string)
bool(true) Class: Stuff. Calling getName method returns "Three" (string)
那是什么意思?您确实不能直接在yield调用中定义返回类型。收益率将始终返回Generator
实例。一种可能的解决方案是使用IteratorIterator
类。
即使您的IDE也可以使用该解决方案。