Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
{remotion-mixins-disclaimer} 

h5. _New Shimmer_ -- a dessert topping AND a floor wax!

A classic example for multiple inheritance is based 
on a Saturday Night Live parody ad starring Chevy Chase: [New Shimmer]. 

Is _New Shimmer_ a dessert topping? Is it a floor wax? The answer is: both!

So we have two classes - {{DessertTopping}} and {{FloorWax}} - and desire to inherit from *both* of them, giving us a _dessert wax_:

{code}
public class DessertTopping
{
  public void TasteGood ()
  {
    Console.WriteLine ("Mmmmmm, tastes terrific!");
  }
} 

public class FloorWax
{
  public void SealFloor ()
  {
    Console.WriteLine ("Dirt, grime, even black heel marks -- wipe clean with a damp mop!");
  }
}
{code}

The result of our mixing will be a new class that is generated by re-motion's factory, which also instantiates that class:

{code}
// generate the class that "inherits" dessert topping members and floor wax members
// and instantiate that class
var myDessertWax = ObjectFactory.Create<DessertTopping> (ParamList.Empty);
{code}

The {{myDessertWax}} instance will be able to execute both the
- {{TasteGood}}
- {{SealFloor}}
methods. 

For this to work, you
- refashion the {{FloorWax}} class as a mixin class
- MUST use the {{ObjectFactory}} to instantiate your mixed class 
  ({{DessertWax}} -- instantiating with {{new}} won't do much in 
  terms of mixing here. 
- create a _mixin configuration_ with attributes, for the {{ObjectFactory}}   to process

It is the {{ObjectFactory}} that learns how you have set up your code and how target classes are extended by mixins classes. The type-paramter {{DessertTopping}} in

{code}
var myDessertWax = ObjectFactory.Create<DessertTopping> (ParamList.Empty);
{code}
 
will have all the information attached to it for the {{ObjectFactory}} to do the right thing and instantiate a _mixed class_, in this case derived from the specified target class {{DessertTopping}} and its mixin. The entirety of all specifications of which mixin classes extend which target classes in an application is called the "mixin configuration" and is determined by reflection when the application starts up ^[determining the mixin configuration]^.  The {{ObjectFactory}} knows the entire mixin configuration, usually  before any part of your actual application code starts to run.  

h5. Putting it all together
By convention, we give a mixin the suffix (wait for it) "Mixin"^["mixin" convention]^. 
A mixin _requires_ an interface for accessing the mixin's members in the instance, in this case the {{SealFloor}} method. 

{code}
public interface IFloorWaxMixin 
{
  void SealFloor ();
}

public class FloorWaxMixin : IFloorWaxMixin
{
  public void SealFloor ()
  {
    Console.WriteLine ("Dirt, grime, even black heel marks -- wipe clean with a damp mop!");
  }
}
{code}

Now all we have to do for the mixing is telling the {{DessertTopping}} class to actually use the {{FloorWaxMixin}} class. You do that with the {{Uses}} attribute:

{code}
[Uses (typeof (FloorWaxMixin))]
public class DessertTopping
{
  public void TasteGood ()
  {
    Console.WriteLine ("Mmmmmm, tastes terrific!");
  }
} 
{code}

Here is the call to {{ObjectFactory}} again:

{code}
var myDessertWax = ObjectFactory.Create<DessertTopping> (ParamList.Empty);
{code}

You can access {{DessertTopping}}'s {{TasteGood}} as expected:
{code}
myDessertWax.TasteGood ();
{code}

For accessing the {{FloorWaxMixin}}'s {{SealFloor}} you must cast to its interface:
{code}
((IFloorWaxMixin) myDessertWax).SealFloor ();
{code}

{panel:bgColor=lightgreen}
Brief detour: [Of what class is the instance the object factory returns?]
{panel}

h5. You need only one interface here

Note that the {{DessertTopping}} class does not need an interface, and that the mixin class {{FloorWaxMixin}} does not know anything about 
the {{DessertTopping}} class. With the {{FloorWaxMixin}} class you can give a {{SealFloor}} method to any class, instantiate it with the 
{{ObjectFactory}} and call the new instance's {{SealFloor}} method 
by casting to the {{IFloorWaxMixin}} interface. 

h5. A target class can use multiple mixins (of course!)

What if a dessert wax is not useful enough and needs even more features? What about adding a shaving cream for more utility and vitamins to make the dessert wax more nutritious? Make it use more mixins:

{code}
[Uses (typeof (FloorWaxMixin))]
[Uses (typeof (ShavingCreamMixin))]
[Uses (typeof (VitaminsMixin))]
public class DessertTopping
{
  public void TasteGood ()
  {
    Console.WriteLine ("Mmmmmm, tastes terrific!");
  }
} 
{code}

Note that each of these mixins also needs an interface, because without one you can't access the mixins member in the instance of the mixed class. For example:

{code}
myShavingVitaminDessertWax.TasteGood ();
((IFloorWaxMixin) myShavingVitaminDessertWax).SealFloor ();
((IShavingCreamMixin) myShavingVitaminDessertWax).LubricateBeardStubbles ();
((IVitaminsMixin) myShavingVitaminDessertWax).BoostImmuneSystem ();
{code}


h5. Don't forget to touch the library

As pointed out in the overview [re-motion mixins front matter -- READ ME], 
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:

{code}
FrameworkVersion.RetrieveFromType (typeof (IMixinTarget));
{code}  

You will see this line in all code samples in this tutorial. It doesn't do anything except taking care that the {{Remotion}} assembly can be loaded properly. If you use re-motion mixin in the context of the larger re-motion framework (re-store, for example), you don't need such a line, because the framework will take care of loading the reference. If you forget that line, you will get a [re-motion mixins TypeInitializationException]. 

h5. Using {{Uses}}

The sample code presented so far would not be considered in good taste in production. For one, the object factory cannot instantiate the 
{{DessertTopping}} class independently of its mixin class -- the {{Uses}} attribute cannot be ignored here. It is much better style to introduce a dedicated class as target class. A natural name in this example is {{DessertWax}}, using both a {{DessertTopping}} and a {{FloorWaxMixin}}. (Note that we assume here that {{DessertTopping}} can be used as a stand-alone class, not only as a mixin class. For this reason we keep the name {{DessertTopping}} and don't rename it to {{DessertToppingMixin}}, as explained in ["mixin" convention].)

What's more, applying the {{Uses}} attribute to the target class is a modification to that target class' source code. It is more practical 
to do it the other way around and attribute the _mixin_ class with {{Extends}}. The next page explains how this works.

h5. Under the hood

The wiki page
 [under the hood of re-motion mixins -- the simplest case -- 'DessertTopping' uses 'FloorWaxMixin']
 explains how data is organized for this simple use of re-motion mixins. The data is organized in exactly the same way for what is discussed next -- [re-motion mixins basics -- the 'Extends' attribute].
 
h5. Sample code

You can find a complete listing for this dessert wax sample [in subversion: 
[|https://svn.re-motion.org/svn/Remotion-Contrib/Samples/Mixin/WikiSamples/trunk/WikiSamples.DessertWaxUses/Program.cs]

h5. See also

- ["mixin" convention]
- [Of what class is the instance the object factory returns?]
- [New Shimmer]
- [determining the mixin configuration]
- [under the hood of re-motion mixins -- the simplest case -- 'DessertTopping' uses 'FloorWaxMixin']