c#-4.0 mixins

c# 4.0 - Mixins con C#4.0



c#-4.0 (4)

He visto varias preguntas con respecto a si los mixins se pueden crear en C # y a menudo se dirigen al proyecto de re-mix en Codeplex. Sin embargo, no sé si me gusta el concepto de "interfaz completa". Idealmente, extendería una clase como esta:

[Taggable] public class MyClass { .... }

Simplemente agregando la interfaz de Taggable, puedo crear objetos de tipo MyClass a través de algún tipo de fábrica de objetos. La instancia devuelta tendría todos los miembros definidos en MyClass, así como todos los miembros proporcionados al agregar el atributo de etiquetado (como una colección de etiquetas). Parece que esto sería fácilmente factible utilizando C # 4.0 (la palabra clave dinámica). El proyecto de re-mezcla usa C # 3.5. ¿Alguien tiene alguna buena manera de extender objetos a través de C # 4.0 sin alterar las clases? Gracias.


He estado trabajando en un framework Mixin de código abierto para C # pMixins . Aprovecha las clases parciales y los generadores de código para conectar en la clase Mixin al destino:

//Mixin - Class that contains members that should be injected into other classes. public class Mixin { // This method should be in several class public void Method(){ } } //Target (Note: That it is partial) - Add members from Mixin [pMixn(Target = typeof(Mixin)] public partial class Target{} //Example of using Target public class Consumer { public void Example() { var target = new Target(); // can call mixed in method target.Method(); // can implicitly convert Target to Mixin Mixin m = new Target(); m.Method(); } }


Puede crear construcciones similares a la mezcla en C # 4.0 sin usar dinámico, con métodos de extensión en interfaces y la clase ConditionalWeakTable para almacenar el estado. Echa un vistazo here para la idea.

Aquí hay un ejemplo:

public interface MNamed { // required members go here } public static class MNamedCode { // provided methods go here, as extension methods to MNamed // to maintain state: private class State { // public fields or properties for the desired state public string Name; } private static readonly ConditionalWeakTable<MNamed, State> _stateTable = new ConditionalWeakTable<MNamed, State>(); // to access the state: public static string GetName(this MNamed self) { return _stateTable.GetOrCreateValue(self).Name; } public static void SetName(this MNamed self, string value) { _stateTable.GetOrCreateValue(self).Name = value; } }

Úsalo así:

class Order : MNamed { // you can list other mixins here... ... } ... var o = new Order(); o.SetName("My awesome order"); ... var name = o.GetName();

El problema de usar un atributo es que no puede pasar parámetros genéricos de la clase a la mezcla. Puedes hacer esto con interfaces de marcadores.


Puede crear un DynamicObject que DynamicObject las llamadas que recibe a una lista de objetivos, en un estilo de cadena de responsabilidad (tenga en cuenta que el envío polimórfico también funciona así: desde la clase más derivada hacia arriba):

public class Composition : DynamicObject { private List<object> targets = new List<object>(); public Composition(params object[] targets) { AddTargets(targets); } protected void AddTargets(IEnumerable<object> targets) { this.targets.AddRange(targets); } public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { foreach (var target in targets) { var methods = target.GetType().GetMethods(); var targetMethod = methods.FirstOrDefault(m => m.Name == binder.Name && ParametersMatch(m, args)); if (targetMethod != null) { result = targetMethod.Invoke(target, args); return true; } } return base.TryInvokeMember(binder, args, out result); } private bool ParametersMatch(MethodInfo method, object[] args) { var typesAreTheSame = method.GetParameters().Zip( args, (param, arg) => param.GetType() == arg.GetType()); return typesAreTheSame.Count() == args.Length && typesAreTheSame.All(_=>_); } }

Tenga en cuenta que también desearía implementar la delegación para propiedades ( TryGetMember y TrySetMember ), indexadores ( TryGetIndex y TrySetIndex ) y operadores ( TryBinaryOperation y TryUnaryOperation ).

Luego, da un conjunto de clases:

class MyClass { public void MyClassMethod() { Console.WriteLine("MyClass::Method"); } } class MyOtherClass { public void MyOtherClassMethod() { Console.WriteLine("MyOtherClass::Method"); } }

Puedes "mezclarlos" todos juntos:

dynamic blend = new Composition(new MyClass(), new MyOtherClass()); blend.MyClassMethod(); blend.MyOtherClassMethod();

También puede extender el objeto dinámico para usar los atributos de las clases u otros tipos de anotaciones para buscar mixins. Por ejemplo, dada esta interfaz de anotación:

public interface Uses<M> where M : new() { }

Puedes tener este DynamicObject :

public class MixinComposition : Composition { public MixinComposition(object target) : base(target) { AddTargets(ResolveMixins(target.GetType())); } private IEnumerable<object> ResolveMixins(Type mainType) { return ResolveMixinTypes(mainType). Select(m => InstantiateMixin(m)); } private IEnumerable<Type> ResolveMixinTypes(Type mainType) { return mainType.GetInterfaces(). Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Uses<>)). Select(u => u.GetGenericArguments()[0]); } private object InstantiateMixin(Type type) { return Activator.CreateInstance(type); } }

Y crea tus "mezclas" de esta manera:

class MyMixin { public void MyMixinMethod() { Console.WriteLine("MyMixin::Method"); } } class MyClass : Uses<MyMixin> { public void MyClassMethod() { Console.WriteLine("MyClass::Method"); } } ... dynamic blend = new MixinComposition(new MyClass()); blend.MyClassMethod(); blend.MyMixinMethod();


Sé que este es un tema antiguo, pero también me gustaría presentar un proyecto de código abierto en el que estoy trabajando actualmente: mixinSharp .

Es una extensión de refactorización basada en Roslyn para Visual Studio 2015 que agrega compatibilidad de mezcla a C # al generar el código de delegación requerido.

Por ejemplo, supongamos que tiene el siguiente código de mezcla que desea reutilizar:

// mixin class with the code you want to reuse public class NameMixin { public string Name { get; set; } public void DoSomething() { } }

Y la clase infantil dada en la que desea incluir su mezcla:

// child class where the mixin should be included public class Person { // reference to the mixin private NameMixin _name = new NameMixin(); }

Si ejecuta el paso de refactorización mixinSharp en el campo NameMixin _name , la extensión agregará automáticamente todo el código de pegamento que se requiere para incluir la mezcla en su clase:

public class Person { // reference to the mixin private NameMixin _name = new NameMixin(); public string Name { get { return _name.Name; } set { _name.Name = value; } } public void DoSomething() => _name.DoSomething(); }

Además de esto, mixinSharp tiene algunas características adicionales como la inyección de constructor para instancias de mixin, implementación de interfaces con mixins y más.

Las fuentes están disponibles en github y los binarios (la extensión compilada de Visual Studio) están disponibles en la mixinSharp .