A few times while reading developer forums I've come across people asking if it possible to have a class member which is both static and virtual. The obvious answer is 'no'. Imagine a hypothetical static and virtual method named Foo. Because static members belong to no particular instance, when we say BaseClass.Foo() there is no way to determine whether we really meant to invoke DerivedClass.Foo(). Therefore it makes no sense to be both static and virtual.
All very interesting, but how does it apply to the Z-machine? I wanted to be able to invoke a particular operation by using a simple array of delegates and accessing them by index (the opcode number). While the eight different versions of the Z-machine define different operations, all instances of a particular version have identical sets. Because of this and the fact that I implement each version as a class, the array of operations for a given version would logically be a static member of that version's class. This led to the situation of eight different static arrays, each one defined in a different class. This was wasteful because they have more operations in common than they have differences. Additionally it was ugly because it forced me to wrap the call to the delegate in a virtual method to ensure the proper array was accessed. Yuck.
After thinking about the situation a little I thought it would be nice to have a static array of delegates to virtual methods, then I could just use a single array in my base class. After unsuccessfully trying to do just that, I ended up creating a single static array in my base class populated with delegates which take a ZMachine parameter and that call a virtual method on the instance passed to it to ensure the proper operation is invoked. This was ugly too because it involved a wrapper method to call the virtual method for each operation. I was able to use lambda syntax to clean it up quite a bit.
The result in my abstract ZMachine class looks like this:
protected delegate void Operation(ZMachine machine);
private static readonly ImmutableArray<Operation> operations = InitializeOperations();
private static ImmutableArray<Operation> InitializeOperations()
{
var op = new ImmutableArray<Operation>.Builder(256);
op[0] = op[32] = op[64] = op[96] = op[192] = z => z.Op0();
op[1] = op[33] = op[65] = op[97] = op[193] = z => z.Op1();
...
op[254] = z => z.Op254();
op[255] = z => z.Op255();
return op.MakeImmutable();
}
The methods Op1, Op2, etc. are virtual methods in the abstract base class with the default behavior of calling an abstract method called InvalidOperation, allowing derived classes to implement their own handling for any illegal operations as well as override only those operations which they care about.
Overall I'm pleased with the result. I ended up with a single array of delegates to invoke, and virtual operations to override, with the only wart being the extra method call using the lambdas.
Showing posts with label virtual. Show all posts
Showing posts with label virtual. Show all posts
Monday, October 5, 2009
Subscribe to:
Posts (Atom)