简体   繁体   中英

C# How to generate an object implementing different interfaces dynamically at runtime?

I'm looking at how to solve a problem and I'm not even sure this might be possible at all in C# & .NET 3.5:

Say I have a limited number of interfaces, each describing a specific, non-related set of methods. Now I have a number real-world devices which each may implement just a subset of these interfaces.

During set-up of comms with these devices they will tell me which capabilities they have. I would now like to create an object implementing the interfaces (each resembling one capability of the device) so that higher up in my application architecture I'm able to:

  • write code against the aforementioned interfaces
  • test if that generated object implements a certain interface to see if certain actions are supported

I'm not sure at all which approach to use towards this problem. Any comments or approaches most welcome!

Use a mocking framework such as Moq , RhinoMocks or TypeMock Isolator

If you're looking to do something lower level, things like Castle DynamicProxy might be a good direction for you.

尝试像LinFu.DynamicObject这样的东西

Maybe you don't need to make this so "dynamic".

Have you checked the Abstract Factory pattern? It seems that basically what you need is to create a concrete implementation for each of your interfaces based on a device type .

You don't need to have a single class implementing lots of interfaces, it is enough to have appropriate implementation of the specific interface when your code requests it.

Each concrete implementation of your abstract factory can generate several interface implementations based on your device type.

Example:

 public interface IDeviceFactory
 {
      ISomething GetSomeInterface();
      ISomethingElse GetSomeOtherInterface();
 }

and then you implement the specific factory for each device:

public class SimpleDeviceFactory : IDeviceFactory
{
     public virtual ISomething GetSomeInterface()
     { return Something.Empty; }

     public virtual ISomethingElse GetSomeOtherInterface()
     { return new SomeSimpleConreteImplementation(); }
}

or maybe:

public class ComplexDeviceFactory : IDeviceFactory
{
     public virtual ISomething GetSomeInterface()
     { return new ComplexStuff(); }

     public virtual ISomethingElse GetSomeOtherInterface()
     { return new EvenMoreComplexStuff(); }
}

And then, finally, you create the right factory for your device:

public class DeviceFactory
{
     public static IDeviceFactory CreateForDevice(IDevice device)
     {
          DeviceType type = device.Type; // or something like this
          switch (type)
          {
              case DeviceType.Simple: 
                 return new SimpleDeviceFactory();

              case DeviceType.Complex: 
                 return new ComplexDeviceFactory();

              default:
                 throw new NotImplementedException();
          }
     }
}

Note that I have also marked IDeviceFactory method implementations as virtual , so that you can easily reuse or override specific interfaces for a specific device.

It is possible, just not easy. You need to make a string which is basically a source file and then create and use a CSharpCodeProvider, which you then order to compile your code. If it works, you can manually access your created objects through reflection.

For the interested, i did it a while back, the details are a bit foggy.

.NET 4 might make it easier, you could use one of the already mentioned framework or go the System.Reflection route. You'll find plenty of samples on the internet.

I've done both in the past. Rolling my own il.Emit stuff and using a framework (Spring.NET in my case). For anything but the trivial stuff, use one of the frameworks.

You can also try with the Re-mix project. One of the main features of this project is create mixins , that lets you add interfaces with implementations and state to other classes. Take a look this example:

using Remotion.Mixins;
using Remotion.TypePipe;
//...

public interface ITargetInterface
{
    void DoSomething();
}
// . . .
public class TargetImplementation : ITargetInterface
{
    public void DoSomething()
    {
        Console.WriteLine("ITargetInterface.DoSomething()");
    }
}
// . . .
public interface IMixinInterfaceA
{
    void MethodA();
}
// . . .
public class MixinImplementationA : IMixinInterfaceA
{
    public void MethodA()
    {
        Console.WriteLine("IMixinInterfaceA.MethodA()");
    }
}
// . . .
public interface IMixinInterfaceB
{
    void MethodB(int parameter);
}

// . . .
public class MixinImplementationB : IMixinInterfaceB
{
    public void MethodB(int parameter)
    {
        Console.WriteLine("IMixinInterfaceB.MethodB({0})", parameter);
    }
}

Then you can merge those types to create a mixin:

var config = MixinConfiguration.BuildFromActive()
            .ForClass<TargetImplementation>()
            .AddMixin<MixinImplementationA>()
            .AddMixin<MixinImplementationB>()
            .BuildConfiguration();
MixinConfiguration.SetActiveConfiguration(config);

Unfortunately, you cannot simply call new on the TargetImplementation and expect a mixin. Instead, you have to ask Re-mix to create a TargetImplementation instance so that it can build a new type to your specification and instantiate it. When it is asked for an instance of TargetImplementation , it will return a mixin containing all of the interfaces and classes combined.

ITargetInterface target = ObjectFactory.Create<TargetImplementation>(ParamList.Empty);

target.DoSomething();

var targetAsMixinA = target as IMixinInterfaceA;
if (targetAsMixinA != null)
{
   targetAsMixinA.MethodA();
}

var targetAsMixinB = target as IMixinInterfaceB;
if (targetAsMixinB != null)
{
    targetAsMixinB.MethodB(30);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM