Какой бы бессмысленной ни была эта конструкция, по какой-то причине она заинтриговала меня, и я быстро смоделировал реализацию Castle DynamicProxy для создания объектов, которые объединяют несколько интерфейсов.
Фабрика mixin предоставляет два метода:
object CreateMixin(params object[] objects)
Возвращает объект, который реализует любое количество интерфейсов.Чтобы добраться до реализованного интерфейса, вы должны привести возвращенный объект к этому интерфейсу.
TMixin CreateMixin<TMixin, T1, T2>(T1 obj1, T2 obj2)
Возвращает интерфейс, который реализует два других интерфейса для достижения строгой типизации.Этот объединяющий интерфейс должен существовать во время компиляции.
Вот объекты:
public interface ICat {
void Meow();
int Age { get; set; }
}
public interface IDog {
void Bark();
int Weight { get; set; }
}
public interface IMouse {
void Squeek();
}
public interface ICatDog : ICat, IDog {
}
public interface ICatDogMouse : ICat, IDog, IMouse {
}
public class Mouse : IMouse {
#region IMouse Members
public void Squeek() {
Console.WriteLine("Squeek squeek");
}
#endregion
}
public class Cat : ICat {
#region ICat Members
public void Meow() {
Console.WriteLine("Meow");
}
public int Age {
get;
set;
}
#endregion
}
public class Dog : IDog {
#region IDog Members
public void Bark() {
Console.WriteLine("Woof");
}
public int Weight {
get;
set;
}
#endregion
}
Обратите внимание на ICatDog
интерфейс. Я подумал, что было бы неплохо, если бы динамический прокси-сервер возвращалчто-то строго типизированное и может быть использовано там, где принят любой интерфейс.Этот интерфейс должен быть определен во время компиляции, если строгая типизация действительно желательна.Теперь для фабрики:
using Castle.DynamicProxy;
public class MixinFactory {
/// <summary>
/// Creates a mixin by comining all the interfaces implemented by objects array.
/// </summary>
/// <param name="objects">The objects to combine into one instance.</param>
/// <returns></returns>
public static object CreateMixin(params object[] objects) {
ProxyGenerator generator = new ProxyGenerator();
ProxyGenerationOptions options = new ProxyGenerationOptions();
objects.ToList().ForEach(obj => options.AddMixinInstance(obj));
return generator.CreateClassProxy<object>(options);
}
/// <summary>
/// Creates a dynamic proxy of type TMixin. Members that called through this interface will be delegated to the first matched instance from the objects array
/// It is up to the caller to make sure that objects parameter contains instances of all interfaces that TMixin implements
/// </summary>
/// <typeparam name="TMixin">The type of the mixin to return.</typeparam>
/// <param name="objects">The objects that will be mixed in.</param>
/// <returns>An instance of TMixin.</returns>
public static TMixin CreateMixin<TMixin>(params object[] objects)
where TMixin : class {
if(objects.Any(o => o == null))
throw new ArgumentNullException("All mixins should be non-null");
ProxyGenerator generator = new ProxyGenerator();
ProxyGenerationOptions options = new ProxyGenerationOptions();
options.Selector = new MixinSelector();
return generator.CreateInterfaceProxyWithoutTarget<TMixin>(options, objects.Select(o => new MixinInterceptor(o)).ToArray());
}
}
public class MixinInterceptor : IInterceptor {
private object m_Instance;
public MixinInterceptor(object obj1) {
this.m_Instance = obj1;
}
public Type ObjectType {
get {
return m_Instance.GetType();
}
}
#region IInterceptor Members
public void Intercept(IInvocation invocation) {
invocation.ReturnValue = invocation.Method.Invoke(m_Instance, invocation.Arguments);
}
#endregion
}
public class MixinSelector : IInterceptorSelector{
#region IInterceptorSelector Members
public IInterceptor[] SelectInterceptors(Type type, System.Reflection.MethodInfo method, IInterceptor[] interceptors) {
var matched = interceptors
.OfType<MixinInterceptor>()
.Where(mi => method.DeclaringType.IsAssignableFrom(mi.ObjectType))
.ToArray();
if(matched.Length == 0)
throw new InvalidOperationException("Cannot match method " + method.Name + "on type " + method.DeclaringType.FullName + ". No interceptor for this type is defined");
return matched;
}
#endregion
}
Использование лучше всего объясняется в этих модульных тестах.Как вы можете видеть, второй метод возвращает безопасный интерфейс типа, который, по-видимому, объединяет любое количество интерфейсов.
[TestMethod]
public void CreatedMixinShouldntThrow() {
ICat obj1 = new Cat();
IDog obj2 = new Dog();
var actual = MixinFactory.CreateMixin(obj1, obj2);
((IDog)actual).Bark();
var weight = ((IDog)actual).Weight;
((ICat)actual).Meow();
var age = ((ICat)actual).Age;
}
[TestMethod]
public void CreatedGeneric3MixinShouldntThrow() {
ICat obj1 = new Cat();
IDog obj2 = new Dog();
IMouse obj3 = new Mouse();
var actual = MixinFactory.CreateMixin<ICatDogMouse>(obj1, obj2, obj3);
actual.Bark();
var weight = actual.Weight;
actual.Meow();
var age = actual.Age;
actual.Squeek();
}
Я написал об этом в блоге более подробно и предоставил источник и тесты.Вы можете найти это здесь .