If I had many different Models that could all, through logic encapsulated by an adapter, produce items of type T, then I might maintain a producer/consumer component through the techniques demonstrated in the code.
When I came up with this, it seemed novel to overlay a producer onto a linked list - the reflection in the Add() method is the most interesting thing to me, because of the way a new adapter is supported through an additional constructor in the derived class.
The concept for reuse is that over time, if I need to place any arbitrary model data in the Producer queue:
- add client code: call producer.Add(AnySource) in client
- create adapter class for typeof(AnySource) to T
- add constructor: DerivedProdcuer(AnySource) - init enumerator
A couple things I think are interesting and was hoping to hear feedback on - they are original ideas to me (surely unoriginal to others).
- the use of reflection in a base class that leverages the polymorphism of "this"
- the combination a factory method and a collection method - Add(object) both creates a new instance of Producer and adds that instance to a linked list of which it is currently the tail of.
void Main()
{
Producer<Foo> producer = new EntityProducer<Foo>();
var fooConsumer = new Consumer<Foo>();
fooConsumer.ItemReady += (sender, item) =>
{
Console.Write(item.Data);
};
var modelA = new ModelA();
modelA.Foos.Add(new Foo { Data = 1 });
modelA.Foos.Add(new Foo { Data = 2 });
modelA.Foos.Add(new Foo { Data = 3 });
producer = producer.Add(modelA);
var modelB = new ModelB();
modelB.Bars.Add(new Bar { Data = "4" });
modelB.Bars.Add(new Bar { Data = "5" });
modelB.Bars.Add(new Bar { Data = "6" });
producer = producer.Add(modelB);
fooConsumer.Consume(producer);
}
class Producer<T>
{
protected IEnumerator<T> _enum;
Producer<T> _parent;
public Producer<T> Add(object obj)
{
Producer<T> res = null;
var constructor = this.GetType().GetConstructor(new [] {obj.GetType()});
res = (Producer<T>)constructor.Invoke(new [] {obj});
res._parent = this;
return res;
}
public IEnumerable<T> Items
{
get
{
if(_parent != null && _parent._enum != null)
foreach (var foo in _parent.Items)
yield return foo;
if(this._enum != null)
foreach (var foo in Produce(this._enum))
yield return foo;
}
}
IEnumerable<T> Produce(IEnumerator target)
{
while(target.MoveNext())
{
yield return (T)target.Current;
}
}
}
class EntityProducer<Foo> : Producer<Foo>
{
public EntityProducer() { }
public EntityProducer(ModelA modelA)
{
_enum = modelA.Foos.Cast<Foo>().GetEnumerator();
}
public EntityProducer(ModelB modelB)
{
_enum = new FooAdapter(modelB).Foos.Cast<Foo>().GetEnumerator();
}
}
class Consumer<T>
{
public delegate void ItemReadyHandler(Consumer<T> sender, T item);
public event ItemReadyHandler ItemReady;
void OnItemReady(T item)
{
if(ItemReady != null)
{
ItemReady(this, item);
}
}
public void Consume(Producer<T> producer)
{
foreach (T item in producer.Items)
{
OnItemReady(item);
}
}
}
class Model
{
}
class Entity
{
}
class ModelA : Model
{
public List<Foo> Foos { get { return _foos; } }
readonly List<Foo> _foos = new List<Foo>();
}
class Foo : Entity
{
public int Data { get; set; }
}
class ModelB : Model
{
public List<Bar> Bars { get { return _bars; } }
readonly List<Bar> _bars = new List<Bar>();
}
class Bar : Entity
{
public string Data { get; set; }
}
class FooAdapter
{
readonly ModelB _modelB;
public FooAdapter(ModelB modelB)
{
_modelB = modelB;
}
public IEnumerable<Foo> Foos
{
get
{
foreach (var bar in _modelB.Bars)
{
int converted;
if(!int.TryParse(bar.Data, out converted))
throw new Exception("Conversion error while adapting Bar to Foo");
yield return new Foo { Data = converted };
}
}
}
}