我有一个包装类,可以使用返回IAsyncCursor
IEnumerable<TResult>
我的程序可以从两个地方获取项目:
IEnumerable<TResult> GetItems()
{
List<TResult> fromA = fromA();
IEnumerable<TResult> fromB = fromB();
var result = fromA.Concat(fromB).ToList();
return result;
}
但我不想将result
存储在内存中,因为它太贵了。
我想将GetItems()
的返回结果更改为返回ResultCollection
,并且能够调用Dispose()
因此,我需要将fromA()
和fromB()
方法的结果更改为ResultCollection
,如下所示
ResultCollection<TResult> GetItems()
{
ResultCollection<TResult> fromA = fromA(); // wrap List<TResult> to ResultCollection
ResultCollection<TResult> fromB = fromB(); // return collection that I can dispose when I need
return new ResultCollection<TResult>(fromA.Concat(fromB).ToList());
}
如何更改ResultCollection
以存储List
构造函数?我需要这个新类符合所有OOP
和SOLID
原则。
我不需要像{/ 1>这样的ResultCollection
内只有另一个构造函数
public ResultCollection(IList<TResult> list)
{
_list = list;
}
答案 0 :(得分:2)
/*
* Shared object containing a map of the world and
* methods for getting/setting coordinates
*/
class World extends Thread
{
public $map;
public $sx;
public $sy;
public $organisms;
// set all map coords to 0
public function __construct($sx, $sy, $p)
{
$map = array();
for( $i = 0; $i < $sx; $i++ )
{
$map[$i] = array();
for( $j = 0; $j < $sy; $j++ )
{
$map[$i][$j] = 0;
}
}
$this->map = $map;
$this->sx = $sx;
$this->sy = $sy;
// create and start organism threads
$this->organisms = array();
for( $i = 0; $i < $p; $i++ )
{
// this won't work because threaded objects created
// within a thread scope that have no external references
// are destroyed in the constructor
$this->makeOrganism($i+1, $i+1, $i+1);
}
}
// produces a new organism, adds to world population
public function makeOrganism($x, $y, $value)
{
if( $x < 1 || $x > $this->sx ) return false;
if( $y < 1 || $y > $this->sy ) return false;
if( $this->getMap($x, $y) != 0 ) return false;
echo "creating new organism $value\n";
$organisms = $this->organisms;
// doesn't work because the world data is decoupled in the new thread
$organisms[] = new Organism($this, $x, $y, $value);
$this->organisms = $organisms;
return true;
}
// assumes valid coords
public function setMap($x, $y, $value)
{
return $this->map[$x-1][$y-1] = $value;
}
// assumes valid coords
public function getMap($x, $y)
{
return $this->map[$x-1][$y-1];
}
public function getSX()
{
return $this->sx;
}
public function getSY()
{
return $this->sy;
}
public function run()
{
for( $i = 0; $i < count($this->organisms); $i++ )
{
echo "starting organism ", $this->value, "\n";
$this->organisms[$i]->start();
}
}
}
/*
* Autonomously running agent accessing shared World map
*/
class Organism extends Thread
{
public $value;
public $world;
public $x;
public $y;
public function __construct(World $world, $x, $y, $value)
{
$this->world = $world;
$this->value = $value;
$this->x = $x;
$this->y = $y;
// assume coordinates are empty
$this->world->setMap($x, $y, $value);
}
// try to move organism by $dx, $dy
public function move($dx, $dy)
{
$x = $this->x + $dx;
$y = $this->y + $dy;
if( $x < 1 || $x > $this->world->getSX() ) return false;
if( $y < 1 || $y > $this->world->getSY() ) return false;
if( $this->world->getMap($x, $y) != 0 ) return false;
$this->world->setMap($x, $y, $this->value);
$this->world->setMap($this->x, $this->y, 0);
$this->x = $x;
$this->y = $y;
return true;
}
public function getValue()
{
return $this->value;
}
public function run()
{
// infinite loop; organisms move randomly about until they die
while( true )
{
echo "running organism ", $this->getValue(), "\n";
// this should operate on the shared object World,
// maintaining consistency and avoiding conflicts between threads
$dx = rand(-1, 1);
$dy = rand(-1, 1);
$this->move($dx, $dy);
// occasionally add an organism to the population by cloning this one
if( rand(0, 100) > 95 )
{
$this->world->makeOrganism($this->x+1, $this->y+1, $this->value+100);
}
// wait random interval, organisms are
// not expected to move all at the same time
$this->wait(1000 + rand(500, 1500));
}
}
}
// initialize shared object
$world = new World(50, 50, 50);
$world->start();
$world->join();
不应该返回GetItems
。它应该返回DeferredResultCollection
。然后你可以做到
IEnumerable<...>
这将是完全懒惰的。
整个班级return fromA.Concat(fromB);
永远不应该暴露给此API的消费者,因为它没有有用的公共成员。 DeferredResultCollection
已经足够了。
事实上,根本不需要课程IEnumerable<...>
。你可以用迭代器替换它。
这是一个充实的版本:
DeferredResultCollection
这总是处理资源,即使在这些示例中:
public static IEnumerable<TResult> CreateDeferredIEnumerable(IAsyncCursor<TResult> _asyncCursor)
{
if (_asyncCursor != null)
{
using (_asyncCursor) { //This is key
for (; _asyncCursor.MoveNextAsync().Result;)
{
foreach (var result in _asyncCursor.Current)
{
yield return result;
}
}
} //The Dispose is always triggered!
}
}
为了支持concat,我们需要确保处置:
1. ((IDisposable)CreateDeferredIEnumerable(...)).Dispose();
1. ((IDisposable)CreateDeferredIEnumerable(...).GetEnumerator()).Dispose();
2. CreateDeferredIEnumerable(...).ToList();
3. CreateDeferredIEnumerable(...).Take(1).ToList();
4. foreach (var x in CreateDeferredIEnumerable(...)) break;
我希望这有效,它很快被黑客攻击。不过,它可以确保工作。
答案 1 :(得分:1)
您可以创建一个名为IDisposableEnumerable
的新界面,该界面具有以下定义:
public interface IDisposableEnumerable<T> : IEnumerable<T>, IDisposable
{
}
然后让你的DeferredResultCollection
类实现这样的接口。
然后你应该创建另一个包含任何IEnumerable<T>
的接口的实现。这样的类可以被称为EnumerableWrapper
,它在Dispose
方法中没有任何作用。
您还需要创建另一个可以使用Composite Pattern将两个(或更多个)IDisposableEnumerable
个对象合并在一起的实现,这个类可以称为CompositeDisposableEnumerable
。
请注意,在调用非最佳的异步方法时,您的DeferredResultCollection
类会阻塞。
您可能还想考虑使用Reactive Extensions for .NET,但这可能会导致设计更改。
<强>更新强>
以下是EnumerableWrapper
和CompositeDisposableEnumerable
的样子:
public class EnumerableWrapper<T> : IDisposableEnumerable<T>
{
private readonly IEnumerable<T> m_Enumerable;
public EnumerableWrapper(IEnumerable<T> enumerable)
{
m_Enumerable = enumerable;
}
public void Dispose()
{
}
public IEnumerator<T> GetEnumerator()
{
return m_Enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable) m_Enumerable).GetEnumerator();
}
}
public class CompositeDisposableEnumerable<T> : IDisposableEnumerable<T>
{
private readonly IDisposableEnumerable<T>[] m_DisposableEnumerables;
public CompositeDisposableEnumerable(params IDisposableEnumerable<T>[] disposable_enumerables)
{
m_DisposableEnumerables = disposable_enumerables;
}
public IEnumerator<T> GetEnumerator()
{
foreach (var disposable_enumerable in m_DisposableEnumerables)
{
foreach (var item in disposable_enumerable)
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Dispose()
{
foreach (var disposable_enumerable in m_DisposableEnumerables)
disposable_enumerable.Dispose();
}
}
<强>更新强>
以下是一个示例用法:
public IDisposableEnumerable<T> GetItems<T>()
{
List<T> collection1 = ....;
DeferredResultCollection<T> collection2 = new DeferredResultCollection<T> (async_cursor);
return new CompositeDisposableEnumerable<T>(new EnumerableWrapper<T>(collection1), collection2);
}
从消费者方面来说:
using(var items = GetItems<string>())
{
//do something with items
} //This will invoke `Dispose` which will be propagated finally to DeferredResultCollection.Dispose and thus to IAsyncCursor.Dispose