Design Pattern advice, writing identical content to 2 seperate locations
I'm working with piece of code that writes IL code to memory, I want to also write the same IL code to an Assembly, lets pretend I'm writing the same content to 2 seperate files for now.
I'm looking for a design solution which allows me to write the same content to 2 seperate entities without duplicating the calls.
The following is difficult to explain, if requested I'll post the source code.
My first solution was the easiest, everytime a write was called to the first file I added a write to the second file as well. All was good until I can across a couple of methods in the writer class which returned an Object, these Objects were then used from calling objects to implement further writing and this is where the problem arose.
How can I do the simple writing to the 2 Files and also ensure that when the Writer class methods are called if they return objects that there is an identical object is created with the different instance for each of my files?
Here's some pseudo code
Writer
{
File firstFile...
File secondFile...
void WritesomeIL(string)
{
firstFile.Write(string);
secondFile.Write(string);
}
Object WritesomeIL(string)
{
//do something
Object o = new Object();
return o;
}
void WritesomeIL(Object o)
{
o.DoSomething();
}
}
Caller
{
Writer _w = new Writer();
void WriteSomeCode()
{
Object o = _w.WriteSomeIL(string);
_w.Emit(o);
}
}
[1631 byte] By [
ireland] at [2007-11-20 10:00:07]

# 1 Re: Design Pattern advice, writing identical content to 2 seperate locations
Why not just derive your own Writer class which internally contains two writers. Your class directs all operations to BOTH. Users of you class only see the ione uniform Writer interface...
# 2 Re: Design Pattern advice, writing identical content to 2 seperate locations
Why not just derive your own Writer class which internally contains two writers. Your class directs all operations to BOTH. Users of you class only see the ione uniform Writer interface...
What he said ;)
# 3 Re: Design Pattern advice, writing identical content to 2 seperate locations
That's what I had but it doesn't work because of the returns. When method within the writer class returns an Object, then the caller uses the object and passes it back to the Writer class through another method.
If my writer has 2 writer-objects withing which do the individual writing all is well until some method returns an Object, this objects data can only be based on one of the writer-objects. So when I return the object from the method it only contains the data for one of the writer-objects and so it is useless.
See the 2 classes below, TreeVisitor calls ILGenerator.DeclareLocal, which returns as LocalBuilder object. This LocalBuilder object is then changed in the TreeVisiitor and the changed LocalBuilder object is passed back through another method ILGenerator.Emit.
If I had 2 instance of the ILGenerator, and simply duplicated the calls on the 2 instance all works until I return the LocalBuilder, I can only return 1 object, this localbuilder would only be valid for one of my ILGenerator instances!!
sealed class ILGeneratorWrapper
{
private ILGenerator _cg;//single writer
public LocalBuilder DeclareLocal(Type type, string name)
{
LocalBuilder result = _cg.DeclareLocal(type);
name = name + _uniqueLabelIndex++;
_nameMap.Add(result, name);
return result;
}
public void Emit(OpCode opcode, LocalBuilder local)
{
Cg.Emit(opcode, local);
}
}
class TreeVisitor
{
public CompiledMethod GenerateCodeAssembly()
{
_cg = new ILGeneratorWrapper(methodbuilder.GetILGenerator(), true);
}
public void VisitGetField(GetFieldExpression op)
{
LocalBuilder temp =Cg.DeclareLocal(op.Parent.Type.AsNativeType, "temp");
Cg.Emit(OpCodes.Stloc, temp);
Cg.Emit(OpCodes.Ldloca, temp);//Pass the LocalBuilder back to the it's source class as temp.
}
}
# 4 Re: Design Pattern advice, writing identical content to 2 seperate locations
You can return multiple objects using an ArrayList, custom collection or List<LocalBuilder> (or simply a LocalBuilder[] array if you don't need dynamic resizing).
Your multi-writer could have a collection of writers and map the result of each into a corresponding collection of results.
# 5 Re: Design Pattern advice, writing identical content to 2 seperate locations
Thanks Andreas, that seems like a bit of a hack to me, could there be a design pattern out there to cure what ails me?
# 6 Re: Design Pattern advice, writing identical content to 2 seperate locations
I'm not familiar with reflection in C#, but from what I've seen, I think the following would be a clean and pretty transparent solution:
ILGenerator gen1 = ...;
ILGenerator gen2 = ...;
ILGenerator gen3 = ...;
// Custom class ILMultiGenerator holds multiple generators. You can achieve the dynamic argument list using "params ILGenerator[] generators"
ILMultiGenerator multiGen = new ILMultiGenerator(gen1, gen2, gen3);
// Custom class MultiLocalBuilder holds one builder for each generator.
MultiLocalBuilder builder = multiGen.DeclareLocal(typeof(string));
// This would perform the method on all builders:
builder.SetLocalSymInfo("stringVar");
// You would also be able to access an individual builder:
// builder[0].SetLocalSymInfo("varString");
// It could even be like this:
// builder[gen1].SetLocalSymInfo("varString");
// (assuming hash codes for the objects are generated in such a way they can be used for hash keys.)
# 7 Re: Design Pattern advice, writing identical content to 2 seperate locations
Here's an actual "proof-of-concept" (although I haven't tried to run it, but it should run fine.)
using System;
using System.Reflection.Emit;
namespace MultiGenerator
{
public class ILMultiGenerator
{
private ILGenerator[] generators;
public ILGenerator this[int i]
{
get { return generators[i]; }
}
public ILMultiGenerator(params ILGenerator[] generators)
{
if (generators == null || generators.Length == 0)
throw new ArgumentException();
this.generators = generators;
}
public MultiLocalBuilder DeclareLocal(Type localType)
{
return DeclareLocal(localType, false);
}
public MultiLocalBuilder DeclareLocal(Type localType, bool pinned)
{
LocalBuilder[] builders = new LocalBuilder[generators.Length];
for (int i = 0; i < generators.Length; i++)
{
builders[i] = generators[i].DeclareLocal(localType, pinned);
}
return new MultiLocalBuilder(builders);
}
}
public class MultiLocalBuilder
{
private LocalBuilder[] builders;
public LocalBuilder this[int i]
{
get { return builders[i]; }
}
public MultiLocalBuilder(params LocalBuilder[] builders)
{
if (builders == null || builders.Length == 0)
throw new ArgumentException();
this.builders = builders;
}
public void SetLocalSymInfo(string name)
{
for (int i = 0; i < builders.Length; i++)
{
builders[i].SetLocalSymInfo(name);
}
}
}
}
# 8 Re: Design Pattern advice, writing identical content to 2 seperate locations
Another pattern which i've used (and seen used) is this.
Assume the object you're passing out is an IWriter. What you can do is define a class like this:
public class ILGeneratorWrapperGroup : ILGeneratorWrapper
{
public List<ILGeneratorWrapper> writers;
...
...
...
}
So you can just add multiple 'objects' to this and pass it back. The rest of your code shouldn't care whether it's an actual ILGeneratorWrapper or an ILGeneratorWrapperGroup. This should allow you to transparently support more than one writer easily.
It's pretty similar to what was mentioned above.
# 9 Re: Design Pattern advice, writing identical content to 2 seperate locations
Andreas,
Thanks for that, your design works for me. Thanks alot.
Mutant Fruit, I haven't had a chance to look at yours yet, I might get back to it, thanks.