re-motion mixins basics -- the 'Extends' attribute
In practice, you probably won't use the Uses
attribute very often. Mixins are a good way to extend framework classes (re-motion classes, for example). This works best when the framework classes don't even know that they are extended, when no modification of their source code is necessary. This is in contrast to the first example, where DessertTopping
is extended by telling it to mix in the FloorWax
mixin class.
A much more practical route for extending target classes is the Extends
attribute. Instead of telling the target class to use a mixin, you tell the mixin class that it extends one or more target classes:
[Extends (typeof (DessertTopping))] public class FloorWaxMixin : IFloorWaxMixin { public void SealFloor () { Console.WriteLine ("Dirt, grime, even black heel marks -- wipe clean with a damp mop!"); } }
The framework code, where the extended DessertTopping
resides, remains unaffected. This is of particular utility if you don't have the source code for the framework. Cluttering framework code with application-specific code is frowned upon by programmers, and rightly so. Therefore, the Extends
attribute is preferable for most cases, even if you DO have the source code for the extended class. Attributing the target class with Uses
CAN be
beneficial, but such cases are fairly rare. Expect to use Extends
much more often.
A mixin can extend multiple target classes, for example
[Extends (typeof (DessertTopping))] [Extends (typeof (IceCream))] [Extends (typeof (Pudding))] public class FloorWaxMixin : IFloorWaxMixin { public void SealFloor () { Console.WriteLine ("Dirt, grime, even black heel marks -- wipe clean with a damp mop!"); } }
Uses
and Extends
are mutually exclusive for the same mixing
Using instances of mixed classes is not influenced by how you mix them, i.e. the code works without regard to who extends whom. Whether you put a Uses
attribute on the target class or the Extends
attribute on the mixin class does not matter. However, never use BOTH for the same mixing. This is redundant, so re-motion mixins will believe you are confused and made a mistake and throw an exception to bring the mistake to your attention:
// *** THIS IS WRONG SAMPLE CODE *** [Uses (typeof (FloorWaxMixin))] public class DessertWax { public void TasteGood () { Console.WriteLine ("Mmmmmm, tastes terrific!"); } } // *** Either use 'Uses' or 'Extends'; using BOTH is a mistake [Extends (typeof (DessertTopping))] public class FloorWaxMixin : IFloorWaxMixin { public void SealFloor () { Console.WriteLine ("Dirt, grime, even black heel marks -- wipe clean with a damp mop!"); } }
The bogus code above will compile and link, but it will result in an exception at run-time, when ObjectFactory
tries to mix and instantiate your broken configuration:
Two instances of mixin DessertWax.Uses.FloorWaxMixin are configured for target type DessertWax.Uses.DessertTopping.
Likewise, it is prohibited to put two or more Uses
or Extends
attributes with the same arguments on the same class.
Sneak preview on re-motion mixins and duck typing
What makes mixins useful is that you can apply them to an arbitrary number of classes, but this flexibility usually goes out the window if your mixin assumes certain members for overriding. However, as we will see later (in re-motion mixins basics -- duck typing), these classes need not to be related in any way. For example, you could have a totally unrelated Hamburger
class with a method that coincidentally has the name TasteGood
, as found in DessertTopping
.
Hamburger
does not need to share anything with the DessertTopping
class, not even an interface. Since re-motion mixins mixes via .NET reflection features, the mixing will work anyway. This duck typing is a hallmark of more dynamic languages like python or ruby, but re-motion mixins make it easy to use in .NET, too.
Sample Code
You find the sample code for this wiki page in subversion