Mixins与C#4.0

Act*_*son 30 mixins c#-4.0

我已经看到了关于是否可以在C#中创建mixins的各种问题,并且它们经常被引导到codeplex上的重新混合项目.但是,我不知道我是否喜欢"完整界面"的概念.理想情况下,我会像这样扩展一个类:

    [Taggable]
    public class MyClass
    {
       ....
    }
Run Code Online (Sandbox Code Playgroud)

通过简单地添加Taggable接口,我可以通过某种对象工厂创建MyClass类型的对象.返回的实例将包含MyClass中定义的所有成员以及通过添加标记属性(如标记集合)提供的所有成员.使用C#4.0(动态关键字)似乎很容易做到这一点.重组项目使用C#3.5.有没有人有任何好方法通过C#4.0扩展对象而不改变类本身?谢谢.

Jor*_*dão 69

您可以在C#4.0中创建类似mixin的构造,而无需使用动态,接口上的扩展方法和ConditionalWeakTable存储状态的类.看看这里的想法.

这是一个例子:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

class Order : MNamed { // you can list other mixins here...
  ...
}

...

var o = new Order();
o.SetName("My awesome order");

...

var name = o.GetName();
Run Code Online (Sandbox Code Playgroud)

使用属性的问题是您无法将类中的泛型参数传递给mixin.您可以使用标记界面执行此操作.

  • 神圣的废话这太棒了.如何投票如此之少?没有多重继承的语言使得编写mixins变得如此困难.这是.NET语言和Java的一大缺点.非常好的博客文章! (8认同)
  • 这种方法很好但不允许混合使用方法的多态变化.你的另一个答案更强大(有趣的是,你可以添加两个答案...) (3认同)

Jor*_*dão 18

您可以创建一个DynamicObject将收到的调用转发到目标列表中的责任样式链(请注意,多态调度也可以这样工作 - 从最派生的类向上):

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

}
Run Code Online (Sandbox Code Playgroud)

请注意,您还需要为属性(TryGetMemberTrySetMember),索引器(TryGetIndexTrySetIndex)和运算符(TryBinaryOperationTryUnaryOperation)实现委派.

然后,给出一组类:

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

class MyOtherClass {
  public void MyOtherClassMethod() {
    Console.WriteLine("MyOtherClass::Method");
  }
}
Run Code Online (Sandbox Code Playgroud)

你可以将它们"混合"在一起:

dynamic blend = new Composition(new MyClass(), new MyOtherClass());
blend.MyClassMethod();
blend.MyOtherClassMethod();
Run Code Online (Sandbox Code Playgroud)

您还可以扩展动态对象以使用类的属性或其他类型的注释来查找mixins.例如,给定此注释界面:

public interface Uses<M> where M : new() { }
Run Code Online (Sandbox Code Playgroud)

你可以这样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);
  }

}
Run Code Online (Sandbox Code Playgroud)

并创建这样的"混合":

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();
Run Code Online (Sandbox Code Playgroud)