[pMixins]

A lightweight open source Mixin and AOP framework for C#


Casting and Conversion (is / as) Operators

This recipe explores how [pMixins] handles implicitly and explicitly casting a Target to one of its Mixin types as well as the impact on the built in is and as operators.

[pMixins] adds Conversion Operators in the Target's code-behind to support implicit and explicit casting. This means the Target can always be converted to one of its Mixins types.

The is and as operators are a different story. Given a Target target and a Mixin mixin, the expression target is typeof(mixin) will always be false. However, if Mixin implements an interface IInterface then target is typeof(IInterface) will always be true. The reason for this difference is because [pMixins] will add every interface a Mixin implements to the Target, but because of Single Inheritance [pMixins] can not force the Target to inherit Mixin. To read more about the limitations and inner workings of the is and as operators see this Stack Overflow article: C# Implicit Conversion Operator and is/as operator .

To overcome these limitations, [pMixins] offers custom Extension Methods that provide is and as functionality that do work with the [pMixins] infrastructure: AsIsHelper.As and AsIsHelper.Is So let's see this in action:

First, let's define a basic interface:

public interface ISomeInterface
{
   string InterfaceMethod();
}

and a basic abstract base class:

public abstract class SomeBaseClass
{
   public abstract string BaseClassMethod();
}

We'll create a Mixin that inherits from SomeBaseClass and implements ISomeInterface:

public class Mixin : SomeBaseClass, ISomeInterface
{
   public override string BaseClassMethod()
   {
      return "Base Class";
   }
   public string InterfaceMethod()
   {
      return "Interface";
   }
}

And here is a Target that mixes in Mixin:

[pMixin(Mixin = typeof(Mixin))]
public partial class CastingAndConversionOperators
{
}

Now that we have the classes / interfaces necessary, let's look at the test class that ties all of these concepts together. Each method is explained in detail below.

[TestFixture]
public class CastingAndConversionOperatorsTest
{
   [Test]
   public void ImplicitConversion()
   {
      ISomeInterface i = new CastingAndConversionOperators();
      Assert.NotNull(i);
      SomeBaseClass c = new CastingAndConversionOperators();
      Assert.NotNull(c);
   }
   [Test]
   public void ExplicitConversion()
   {
      Assert.NotNull((ISomeInterface)new CastingAndConversionOperators());
      Assert.NotNull((SomeBaseClass)new CastingAndConversionOperators());
   }
   [Test]
   public void AsOperator()
   {
      var target = new CastingAndConversionOperators();
      //Example of 'as' in action
      Assert.NotNull(((object)new Mixin()) as SomeBaseClass);
      //Unfortunately, there's no way to do this with classes:
      //http://stackoverflow.com/questions/18390664/c-sharp-implicit-conversion-operator-and-is-as-operator
      Assert.Null(((object)target) as SomeBaseClass);
      Assert.NotNull(target.As<SomeBaseClass>());
      //Works on interfaces just fine
      Assert.NotNull(target as ISomeInterface);
   }
   [Test]
   public void IsOperator()
   {
      var target = new CastingAndConversionOperators();
      //Example of 'is' in action
      Assert.True(((object)new Mixin()) is SomeBaseClass);
      //Unfortunately, there's no way to do this with classes:
      //http://stackoverflow.com/questions/18390664/c-sharp-implicit-conversion-operator-and-is-as-operator
      Assert.False(target is SomeBaseClass);
      Assert.True(target.Is<SomeBaseClass>());
      //Works on interfaces just fine
      Assert.True(target is ISomeInterface);
   }
}
ImplicitConversion

This test proves that the CastingAndConversionOperators can be implicitly cast as both Mixin's implemented interfaces and inherited base class.

ExplicitConversion

This test proves that the CastingAndConversionOperators can be explicitly cast as both Mixin's implemented interfaces and inherited base class.

AsOperator

This test shows the operations of the as and AsIsHelper.As. It first demonstrates that as works fine against Mixin and the class SomeBaseClass. It then shows that it does not work against CastingAndConversionOperators and the class SomeBaseClass. Then it shows that AsIsHelper.As does work. Finally, it shows that as works fine for CastingAndConversionOperators and the interface ISomeInterface.

IsOperator

This test shows the operations of the is and AsIsHelper.Is. It first demonstrates that is works fine against Mixin and the class SomeBaseClass. It then shows that it does not work against CastingAndConversionOperators and the class SomeBaseClass. Then it shows that AsIsHelper.Is does work. Finally, it shows that is works fine for CastingAndConversionOperators and the interface ISomeInterface.

Going Further

AsIsHelper is actually a static wrapper exposing an instance of IAsIsImplementation, which does the real work. This means it is possible to provide a custom implementation for checking is and as operations.

The CustomAsIsHelper does just that below:

public class CustomAsIsHelper : AsIsHelper.IAsIsImplementation
{
   public T As<T>(object obj) where T : class
   {
      throw new NotImplementedException();
   }
   public bool Is<T>(object obj)
   {
      throw new NotImplementedException();
   }
}

For CustomAsIsHelper to be wired into the [pMixins] infrastructure, it's is necessary to register with the AsIsHelper. This is shown below:

public static class CustomAsIsHelperRegister
{
   public static void Register()
   {
      AsIsHelper.AsIsImplementationFactory = () => new CustomAsIsHelper();
   }
}

Download the source for this and all other recipes Here!