Runtime multiple inheritance with impromptu-interface

539 views Asked by At

I'm trying to get runtime multiple inheritance to work using impromptu-interface but I'm stuck when I want to pass the object along to a method.

public interface IEngine {
    void Foo();
}

public interface IWheels {
    void Foo();
}

public interface IChassie {
    void Foo();
}

public interface IPaintShop {
    void PaintWheels(IWheels wheels);
    void PaintChassie(IChassie chassie);
    void ChromeEngine(IEngine engine);
}

var paintShop = Impromptu.ActLike<IPaintShop>();
var car = Impromptu.ActLike(new [] {typeof(IEngine), typeof(IWheels), typeof(IChassie) } ); 
// dynamic car = Impromptu.ActLike(new [] {typeof(IEngine), typeof(IWheels), typeof(IChassie) } ); // Same error 
paintShop.PaintWheels(car); // RuntimeException as car is dynamic and not the expected IWheels

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : The best overloaded method match for 'MyStuff.PaintWheels(IWheels)' has some invalid arguments

I've tried to cast but get InvalidCastException:

paintShop.PaintWheels((IWheels)car);

System.InvalidCastException : Unable to cast object of type 'ImpromptuInterface.ActLikeCaster' to type 'MyStuff.IWheels'.

The following works but I'm not sure this is the correct way; it seem unwarranted to convert car to IWheels when IWheels interface should already be inherited:

var wheels = Impromptu.CoerceConvert(car, typeof (IWheels));
paintShop.PaintWheels(wheels);

What is the correct way of achieving runtime multiple inheritance using impromptu-interface?

1

There are 1 answers

4
AGB On BEST ANSWER

The issues you are running into are all related to type safety--even when using a library like Impromptu, you have to be sure the compiler and the runtime are sure the object you are passing into the method is the type that method requires.

ActLike<T> can implement many interfaces, but it only returns a single typed instance of T, so without a type that tells the compiler that your instance implements several interfaces, you will be forced to cast to the necessary interfaces.

Also, ImpromptuInterface allows you to wrap an object with an interface that informally matches up with the implementation of that object, even if the interface was not formally declared. As a consumer of that library, you still have to provide the implementations for the library to wrap.

Try something like the following:

using System;
using ImpromptuInterface;
using ImpromptuInterface.Dynamic;

namespace Example
{
    public interface IEngine
    {
        void Foo();
    }

    public interface IWheels
    {
        void Foo();
    }

    public interface IChassie
    {
        void Foo();
    }

    public interface IPaintShop
    {
        void PaintWheels(IWheels wheels);
        void PaintChassie(IChassie chassie);
        void ChromeEngine(IEngine engine);
    }

    internal class Program
    {
        public static void Main(string[] args)
        {
            var ps = new
            {
                PaintWheels = ReturnVoid.Arguments<IWheels>(wheels => wheels.Foo()),
                PaintChassie = ReturnVoid.Arguments<IChassie>(chassie => chassie.Foo()),
                ChromeEngine = ReturnVoid.Arguments<IEngine>(engine => engine.Foo())
            };
            var paintShop = ps.ActLike<IPaintShop>();

            var fullCar = new
            {
                Foo = ReturnVoid.Arguments(() => Console.WriteLine("Hello World!"))
            };

            var car = fullCar.ActLike<IEngine>(typeof(IChassie),typeof(IWheels));

            //each of these 3 calls prints "Hello World!" to the console
            paintShop.PaintWheels((IWheels)car);//need to tell the compiler to cast your car to type IWheels because var car is of type IEngine
            paintShop.PaintChassie(car as IChassie);//need to tell the compiler to cast your car to type IChassie because var car is of type IEngine
            paintShop.ChromeEngine(car);//works sans cast because var car is of type IEngine

            //each of these 3 calls prints "Hello World!" to the console, too
            dynamic dynamicCar = car;
            paintShop.PaintWheels(dynamicCar);//by using dynamic you disable the compile time
            paintShop.PaintChassie(dynamicCar);//type checking and the compiler "trusts you" on the typing
            paintShop.ChromeEngine(dynamicCar);//since Impromptu wrapped your object and implemented the interfaces for you, there is no runtime exception

            Console.ReadLine();
        }
    }
}