advanced re-motion mixins -- dessert wax revisited
Let's say we want to build our own configuration from scratch for mixing a floor wax to a dessert topping, as demonstrated in the first exercise of this tutorial – re-motion mixins basics -- the 'Uses' attribute and the 'ObjectFactory'. To this end, we create an empty mixin configuration builder with
MixinConfiguration.BuildNew ()
MixinConfiguration
is our gateway to the re-motion mixin library; BuildNew
is a static method that gives you an emtpy builder object. This is not the mixin configuration, it is an intermediate represenation for convenient building. After getting our empty mixin configuration builder, we can add items to it by gluing commands together like this:
MixinConfiguration.BuildNew () // set up new builder .ForClass<TargetClass1> ().AddMixin<MixinClass1> ().AddMixin<MixinClass2> ().AddMixinClass<MixinClass3> () // mix mixin 1, 2 and 3 to target class 1 .ForClass<TargetClass2> ().AddMixin<MixinClass4> ().AddMixin<MixinClass5> () // mix mixin 4 and 5 to target class 2 ... .BuildConfiguration (); // make actual configuration from builder
This manner of piling stuff on an instance is a well-established pattern called the fluent interface pattern. Note the last command in the chain, the concluding .BuildConfiguration
. This method creates the actual mixin configuration from the builder representation. The builder is required to make the convenient mechanics of the fluent interface pattern work.
For our dessert wax example, this means:
var myMixinConfiguration = MixinConfiguration.BuildNew () // make builder .ForClass<DessertTopping> ().AddMixin<FloorWax> () // add items to the builder by chaining methods (= fluent interface) .BuildConfiguration (); // run builder to make mixin configuration
Since we build the mixin configuration ourselves, there is no need to attribute any of the involved classes. What we DO have to do is to set the freshly constructed mixin configuration active for the current thread:
MixinConfiguration.SetActiveConfiguration (myMixinConfiguration); // only affects this thread
From that point on, our nameless mixed class can be exercised as in the examples on previous pages of this tutorial. Here is a complete listing.
// Note that we don't use a [Uses] attribute. // We don't need any attributes here, because all // configuration work is done via the fluent interface // (see listing below). public class DessertTopping { public void TasteGood () { Console.WriteLine ("Mmmmmm, tastes terrific!"); } } public interface IFloorWaxMixin { void SealFloor (); } // Note that we don't use an [Extends] attribute. // The method chaining (fluent interface, see below) // does all the configuration work. public class FloorWaxMixin : IFloorWaxMixin { public void SealFloor () { Console.WriteLine ("Dirt, grime, even black heel marks -- wipe clean with a damp mop!"); } } class Program { static void Main (string[] args) { // you must force the .NET runtime to load a reference to the // Remotion.dll assembly. Otherwise the compiler will remove the // facilities for loading the Remotion.dll assembly and your application // will throw a type initializer exception, because it can't load it. // The easiest way to touch the assembly is to use the IMixinTarget // identifier: FrameworkVersion.RetrieveFromType (typeof (IMixinTarget)); // instantiate and build a new mixin configuration. // In the previous samples, we've done that by using // attributes. var myMixinConfiguration = MixinConfiguration.BuildNew () .ForClass<DessertTopping> ().AddMixin<FloorWaxMixin> () // mix 'FloorWaxMixin' to 'DessertTopping' .BuildConfiguration (); // make that mixin configuration active for this thread MixinConfiguration.SetActiveConfiguration (myMixinConfiguration); // this is same old same old... instantiate mixed class var myDessertWax = ObjectFactory.Create<DessertTopping> (ParamList.Empty); myDessertWax.TasteGood (); ((IFloorWaxMixin) myDessertWax).SealFloor (); Console.ReadLine (); } }
Sample code
You find the sample code for this exercise in subversion.