是否有可能在C#中实现混合?
我听说扩展方法是可能的,但是我自己也搞不清楚。 如果可能的话,我想看一个具体的例子。
谢谢!
这真的取决于你的“mixin”是什么意思 – 每个人似乎都有一个略有不同的想法。 我想看到的mixintypes(但在C#中不可用)正在使实现直通组合变得简单:
public class Mixin : ISomeInterface { private SomeImplementation impl implements ISomeInterface; public void OneMethod() { // Specialise just this method } }
编译器只需通过代理每个成员“impl”来实现ISomeInterface,除非直接在类中有另一个实现。
这一切都不可能在目前虽然:)
有一个开源的框架,使您可以通过C#实现混合。 看看http://remix.codeplex.com/ 。
用这个框架来实现混合是很容易的。 只需看一下样本和页面上的“附加信息”链接。
我通常使用这种模式:
public interface IColor { byte Red {get;} byte Green {get;} byte Blue {get;} } public static class ColorExtensions { public static byte Luminance(this IColor c) { return (byte)(c.Red*0.3 + c.Green*0.59+ c.Blue*0.11); } }
我在同一个源文件/命名空间中有两个定义。 这样,当使用接口时(使用''''),扩展总是可用的。
这给你一个有限的mixin ,如CMS的第一个链接中所述。
限制:
- 没有数据字段
- 没有属性(你必须调用myColor.Luminance()括号, 扩展属性的任何人?)
这在很多情况下仍然是足够的。
如果他们(MS)可以添加一些编译器魔术来自动生成扩展类,那将是很好的:
public interface IColor { byte Red {get;} byte Green {get;} byte Blue {get;} // compiler generates anonymous extension class public static byte Luminance(this IColor c) { return (byte)(c.Red*0.3 + c.Green*0.59+ c.Blue*0.11); } }
尽pipeJon提出的编译器技巧会更好。
LinFu和Castle的 DynamicProxy实现了混合。 COP(复合导向编程)可以被认为是一个混合模式的完整范例。 这个来自Anders Noras的文章有很多有用的信息和链接。
编辑:这是所有可能与C#2.0,没有扩展方法
你也可以增加扩展方法的方式来合并状态,这与WPF的附加属性无异。
这是一个最小样板的例子。 请注意,在目标类上不需要修改,包括添加接口,除非需要多态地处理目标类 – 在这种情况下,最终会得到与实际的多重inheritance非常接近的内容。
// Mixin class: mixin infrastructure and mixin component definitions public static class Mixin { // ===================================== // ComponentFoo: Sample mixin component // ===================================== // ComponentFooState: ComponentFoo contents class ComponentFooState { public ComponentFooState() { // initialize as you like this.Name = "default name"; } public string Name { get; set; } } // ComponentFoo methods // if you like, replace T with some interface // implemented by your target class(es) public static void SetName<T>(this T obj, string name) { var state = GetState(component_foo_states, obj); // do something with "obj" and "state" // for example: state.Name = name + " the " + obj.GetType(); } public static string GetName<T>(this T obj) { var state = GetState(component_foo_states, obj); return state.Name; } // ===================================== // boilerplate // ===================================== // instances of ComponentFoo's state container class, // indexed by target object static readonly Dictionary<object, ComponentFooState> component_foo_states = new Dictionary<object, ComponentFooState>(); // get a target class object's associated state // note lazy instantiation static TState GetState<TState>(Dictionary<object, TState> dict, object obj) where TState : new() { TState ret; if(!dict.TryGet(obj, out ret)) dict[obj] = ret = new TState(); return ret; } }
用法:
var some_obj = new SomeClass(); some_obj.SetName("Johny"); Console.WriteLine(some_obj.GetName()); // "Johny the SomeClass"
请注意,它也适用于null实例,因为扩展方法自然是这样。
您也可以考虑使用WeakDictionary实现来避免由于集合将类引用作为关键字而引起的内存泄漏。
我需要类似的东西,所以我想出了以下使用Reflection.Emit。 在下面的代码中,dynamic生成一个新types,它有一个types为“mixin”的私有成员。 所有对“mixin”接口方法的调用都被转发给这个私有成员。 定义了一个参数构造函数,该构造函数接受一个实现“mixin”接口的实例。 基本上,它等同于为给定的具体typesT和接口I编写以下代码:
class Z : T, I { I impl; public Z(I impl) { this.impl = impl; } // Implement all methods of I by proxying them through this.impl // as follows: // // I.Foo() // { // return this.impl.Foo(); // } }
这是class级:
public class MixinGenerator { public static Type CreateMixin(Type @base, Type mixin) { // Mixin must be an interface if (!mixin.IsInterface) throw new ArgumentException("mixin not an interface"); TypeBuilder typeBuilder = DefineType(@base, new Type[]{mixin}); FieldBuilder fb = typeBuilder.DefineField("impl", mixin, FieldAttributes.Private); DefineConstructor(typeBuilder, fb); DefineInterfaceMethods(typeBuilder, mixin, fb); Type t = typeBuilder.CreateType(); return t; } static AssemblyBuilder assemblyBuilder; private static TypeBuilder DefineType(Type @base, Type [] interfaces) { assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.RunAndSave); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(Guid.NewGuid().ToString()); TypeBuilder b = moduleBuilder.DefineType(Guid.NewGuid().ToString(), @base.Attributes, @base, interfaces); return b; } private static void DefineConstructor(TypeBuilder typeBuilder, FieldBuilder fieldBuilder) { ConstructorBuilder ctor = typeBuilder.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, new Type[] { fieldBuilder.FieldType }); ILGenerator il = ctor.GetILGenerator(); // Call base constructor ConstructorInfo baseCtorInfo = typeBuilder.BaseType.GetConstructor(new Type[]{}); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, typeBuilder.BaseType.GetConstructor(new Type[0])); // Store type parameter in private field il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, fieldBuilder); il.Emit(OpCodes.Ret); } private static void DefineInterfaceMethods(TypeBuilder typeBuilder, Type mixin, FieldInfo instanceField) { MethodInfo[] methods = mixin.GetMethods(); foreach (MethodInfo method in methods) { MethodInfo fwdMethod = instanceField.FieldType.GetMethod(method.Name, method.GetParameters().Select((pi) => { return pi.ParameterType; }).ToArray<Type>()); MethodBuilder methodBuilder = typeBuilder.DefineMethod( fwdMethod.Name, // Could not call absract method, so remove flag fwdMethod.Attributes & (~MethodAttributes.Abstract), fwdMethod.ReturnType, fwdMethod.GetParameters().Select(p => p.ParameterType).ToArray()); methodBuilder.SetReturnType(method.ReturnType); typeBuilder.DefineMethodOverride(methodBuilder, method); // Emit method body ILGenerator il = methodBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, instanceField); // Call with same parameters for (int i = 0; i < method.GetParameters().Length; i++) { il.Emit(OpCodes.Ldarg, i + 1); } il.Emit(OpCodes.Call, fwdMethod); il.Emit(OpCodes.Ret); } } }
这是用法:
public interface ISum { int Sum(int x, int y); } public class SumImpl : ISum { public int Sum(int x, int y) { return x + y; } } public class Multiply { public int Mul(int x, int y) { return x * y; } } // Generate a type that does multiply and sum Type newType = MixinGenerator.CreateMixin(typeof(Multiply), typeof(ISum)); object instance = Activator.CreateInstance(newType, new object[] { new SumImpl() }); int res = ((Multiply)instance).Mul(2, 4); Console.WriteLine(res); res = ((ISum)instance).Sum(1, 4); Console.WriteLine(res);
如果您有一个可以存储数据的基类,则可以强制编译器安全并使用标记接口。 从接受的答案中可以看出,“C#3.0中的Mixins”或多或less是这样提出的。
public static class ModelBaseMixins { public interface IHasStuff{ } public static void AddStuff<TObjectBase>(this TObjectBase objectBase, Stuff stuff) where TObjectBase: ObjectBase, IHasStuff { var stuffStore = objectBase.Get<IList<Stuff>>("stuffStore"); stuffStore.Add(stuff); } }
ObjectBase:
public abstract class ObjectBase { protected ModelBase() { _objects = new Dictionary<string, object>(); } private readonly Dictionary<string, object> _objects; internal void Add<T>(T thing, string name) { _objects[name] = thing; } internal T Get<T>(string name) { T thing = null; _objects.TryGetValue(name, out thing); return (T) thing; }
所以,如果你有一个类,你可以inheritance“ObjectBase”,并用IHasStuff装饰,你现在可以添加sutff
这里是我刚刚提出的一个混合实现。 我可能会用它与我的图书馆 。
这可能是在某个地方完成的。
这是所有静态types,没有字典或东西。 它需要每种types的额外代码,每个实例不需要任何存储。 另一方面,如果你愿意的话,它也可以让你灵活地改变mixin的实现。 没有后期构build,预构build,中期构build工具。
它有一些限制,但它确实允许重写等等。
我们从定义标记界面开始。 也许以后会添加一些东西:
public interface Mixin {}
这个接口是由mixin实现的。 Mixins是常规类。 types不直接inheritance或实现mixin。 他们只是使用界面公开一个mixin的实例:
public interface HasMixins {} public interface Has<TMixin> : HasMixins where TMixin : Mixin { TMixin Mixin { get; } }
实现这个接口意味着支持mixin。 明确地实施它是重要的,因为我们将有每种types的几个。
现在使用扩展方法一个小技巧。 我们定义:
public static class MixinUtils { public static TMixin Mixout<TMixin>(this Has<TMixin> what) where TMixin : Mixin { return what.Mixin; } }
Mixout
暴露了适当types的混合。 现在,为了testing这个,我们定义:
public abstract class Mixin1 : Mixin {} public abstract class Mixin2 : Mixin {} public abstract class Mixin3 : Mixin {} public class Test : Has<Mixin1>, Has<Mixin2> { private class Mixin1Impl : Mixin1 { public static readonly Mixin1Impl Instance = new Mixin1Impl(); } private class Mixin2Impl : Mixin2 { public static readonly Mixin2Impl Instance = new Mixin2Impl(); } Mixin1 Has<Mixin1>.Mixin => Mixin1Impl.Instance; Mixin2 Has<Mixin2>.Mixin => Mixin2Impl.Instance; } static class TestThis { public static void run() { var t = new Test(); var a = t.Mixout<Mixin1>(); var b = t.Mixout<Mixin2>(); } }
相当有趣(尽pipe回顾起来,它确实有道理),IntelliSense没有检测到扩展方法Mixout
适用于Test
,但编译器确实接受它,只要Test
实际上有mixin。 如果你试试,
t.Mixout<Mixin3>();
它给你一个编译错误。
你可以去看看,也可以定义下面的方法:
[Obsolete("The object does not have this mixin.", true)] public static TSome Mixout<TSome>(this HasMixins something) where TSome : Mixin { return default(TSome); }
这是做什么的,a)在智能感知中显示一个名为Mixout
的方法,提醒你它的存在,以及b)提供一些更具描述性的错误信息(由Obsolete
属性产生)。