2008-11-01 174 views

回答

87

这真的取决于你所说的“mixin” - 每个人似乎都有一个略有不同的想法。那种混入的我倒是喜欢看到(但不可用在C#中)正在实施,通过构图简单:

public class Mixin : ISomeInterface 
{ 
    private SomeImplementation impl implements ISomeInterface; 

    public void OneMethod() 
    { 
     // Specialise just this method 
    } 
} 

,编译器将实现ISomeInterface只是每个成员代理到“ impl“,除非班上另有实施。

无的,这是可能的此刻虽然:)

+0

很酷的技术......我一定会为这项技术投票。 – 2010-02-10 08:46:20

+11

安德斯请将此添加到C#5! – Schneider 2010-08-26 05:51:19

+48

我觉得很烦人的是,C++专家发表类似于“更喜欢组合继承”的语句,但语言(C++或C#)为“正确的事情”提供了宝贵的帮助。 – 2010-09-27 14:10:05

7

有一个开源框架,使您能够通过C#实现的混入。看看http://remix.codeplex.com/

这个框架很容易实现mixin。只需查看样本和页面上的“附加信息”链接即可。

5

我通常采用以下模式:

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()带括号的,extension properties人?)

它仍足以让许多的情况。

这将是很好,如果他们(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); 
    } 
} 

尽管乔恩提出的编译器把戏会更好。

3

你也可以增加扩展方法的方式来合并状态,这与WPF的附加属性无异。

这是一个最小样板的例子。请注意,目标类不需要修改,包括添加接口,除非需要多态处理目标类 - 在这种情况下,最终会得到与实际多重继承非常接近的内容。

// 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" 

注意,它也可以使用空的情况下,因为扩展方法做自然。

您可能还会考虑使用WeakDictionary实现来避免由集合持有将目标类引用作为键所导致的内存泄漏。

2

我需要类似的东西,所以我想出了以下使用Reflection.Emit。在下面的代码中,动态生成了一个新类型,它有一个类型为'mixin'的私有成员。所有对'mixin'接口方法的调用都被转发给这个私有成员。定义了一个参数构造函数,该构造函数接受一个实现“mixin”接口的实例。基本上,它是等于写入下述代码对于给定的具体类型T和接口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(); 
    // } 
} 

这是类:

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); 
1

如果你有一个可以存储数据的基类,你可以强制编译器安全并使用标记接口。 这或多或少是从接受的答案中提出的“Mixin in C#3.0”。

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; 
    } 

所以,如果你有一个类可以继承“ObjectBase”,并与IHasStuff装点您可以添加某些材料现在

0

这是一个混合的实现我”我刚刚想出来。我可能会用它a library of mine

它可能已经完成之前,某处。

这是所有静态类型,没有字典或东西。它需要每种类型的额外代码的一点点,你不需要每个实例的任何存储。另一方面,如果您愿意,它还可以灵活地改变即时混音实现。没有后期构建,预构建和中期构建工具。

它有一些限制,但它确实允许重写等内容。

我们从定义标记接口开始。稍后可能会添加一些内容:

public interface Mixin {} 

此接口由mixins实现。 Mixins是常规类。类型不直接继承或实现mixin。他们只是使用接口公开一个mixin的实例:

public interface HasMixins {} 

public interface Has<TMixin> : HasMixins 
    where TMixin : Mixin { 
    TMixin Mixin { get; } 
} 

实现此接口意味着支持mixin。明确实施它很重要,因为我们将为每种类型提供几种这样的产品。

现在使用扩展方法的一个小技巧。我们定义:

public static class MixinUtils { 
    public static TMixin Mixout<TMixin>(this Has<TMixin> what) 
     where TMixin : Mixin { 
     return what.Mixin; 
    } 
} 

Mixout暴露了适当类型的mixin。现在,测试了这一点,让我们定义:

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>(); 
    } 
} 

而可笑的(虽然现在回想起来,这有一定道理),智能感知不检测的扩展方法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); 
} 

这样做是什么,一个)显示一个名为智能感知Mixout方法,提醒你它的存在,和b)提供一个更具描述性的错误信息(由Obsolete属性生成)。

相关问题