【问题标题】:How to build a mix-in architecture framework in C#?如何在 C# 中构建一个混合架构框架?
【发布时间】:2010-12-01 17:39:28
【问题描述】:

我有一个想要构建的控件框架的概念。这个想法的不同之处在于,我不打算采用“一刀切”的方法或“一个控制来统治所有人”的方法。

例如,Telerik 制作了一个非常好的 Grid 控件,ComponentOne、Xceed 等也是如此。但是,它们都是具有成百上千个方法和属性、复杂对象模型层次结构等的巨大控件……所有很多时候,这些网格对于你需要的东西来说太过分了,但你仍然必须承担学习整个网格来做一些简单的事情的艰巨任务。

我的概念更像是一种“混合”方法。您可以在其中创建一个非常简单的控件,然后构建您可以“添加”到该控件的功能点菜。例如,您有一个简单的网格,并且您想为每个网格添加带有页眉和页脚的“部分”。

好的,那么问题出在哪里?做这种事情的传统方法是通过多重继承,C# 不支持。即使它确实支持它,我仍然认为 MI 增加的问题多于它解决的问题。

所以我正在征求 SO 关于如何解决这个问题的意见。 MEF 会是一个潜在的解决方案吗?

编辑:

我想到的是,可以使用表达式树从各种表达式构建控件。我必须再考虑一下,但这是一个有趣的概念。

另一个可能的选项可能是“控制生成器”,它基于选定的特征生成装配。这似乎更复杂,但使用 T4 可能是可控的。

【问题讨论】:

  • @Robert Harvey - 我已经在很大程度上拒绝了扩展方法,因为它们不能真正提供额外的状态,只能提供方法。您引用的技术需要最终用户实现所有其他状态,而我并不真正热衷于此。

标签: c# frameworks mixins


【解决方案1】:

几个选项

  1. 考虑装饰器模式;扩展您可以在运行时构建的极简类的行为的小对象。 Dotnet 框架中的典型示例将围绕 IO 流(例如 StreamReader/Writer、TextStreamReader/Writer、FileStreamReader/Writer);它在 OO UI 框架中也很常见,典型示例类似于 var window=new HorizontalScrollingWindowDecorator(new VerticalScrollingWindowDecorator(new Window));

  2. 状态、策略、命令和复合模式为处理行为组合提供了各种替代方案,而无需大量控制流。例如,您将某些类型的行为委托给对象的当前状态(您的某些类方法基本上调用当前对象状态的方法)。您还可以将行为委托给命令对象,或使用复合命令构建复杂命令。单继承通常会导致您更喜欢对象的组合而不是继承,这些都是组合行为的经典模式,适用于单继承。

  3. 当某些小部分行为需要由特定实例实现时,您可以将委托用于函数式编程中常见的“中间漏洞”模式。

  4. 您可以使用 PostSharp 或类似的面向方面的编程工具来执行运行时或编译时的行为编织,特别适用于多个不同类所需的行为与该类的主要职责。

【讨论】:

    【解决方案2】:

    我个人认为多重继承无论如何都是实现这种设计的不好方法。大多数时候,多重继承似乎是一个糟糕的设计。继承远远僵化了组件之间的关系。

    组合要灵活得多,在这种情况下,MEF 将是一个很好的起点。

    您的控件的基类将是一个行为容器——本质上是一个 MEF 容器。

    它仍然留下了其他类如何与您的控件交互的问题。有很多选择。例如基础控件上的标准接口、消息传递,甚至在 C# 4.0 中使用动态绑定。

    【讨论】:

    • 我同意 MI。我只是提到这是处理问题的“传统”方式。也许我应该更多地调查 MEF。我希望有更多经验的人能评论这种分析方法是否有益。
    • 我在 Web 应用程序中使用过 MEF 非常简单。从我所见,我会说它设计得非常好,并且允许非常灵活的组件链接。但是,您必须考虑如何将 MEF 的插件/可交换特性与控件的标记结合起来。您需要一些东西来解析标记 - 并且这段代码可能必须非常了解它正在解析标记的控件,这可能会否定对 MEF 的需求。
    【解决方案3】:

    我想尝试回答,但不评论这是否是一个值得解决的问题。

    我还没有为组件尝试过这个,但理论上有Decorator Pattern:您可以在运行时添加插件来拦截控件的(键盘和鼠标)输入和(屏幕)输出。

    【讨论】:

    • 我想过,但是如果你使用属性来实现装饰,它会很快变得非常丑陋,而且我认为所需的反射会导致显着的性能损失。
    • 对..这是我对 MEF 的想法。但是,我对 MEF 的了解还不够,无法知道这种方法是否实用。或者如果我必须开发自己的代码注入方法。
    【解决方案4】:

    WPF 允许您将逻辑元素的可视化表示与数据模板组合在一起,从而解决了这个问题。 Control 类本身不是通过混合功能来创建新的 Control 类,而是通过合并各种类型的内容的能力提供扩展点,并使用丰富的模板系统来安排和可视化内容。

    换句话说,仍然可以说是“一个控制来统治一切”,但控制是一个骨架,而不是一个完整的身体。脚手架提供了一个易于理解的框架,可以在其中添加功能,而模板系统则提供了充实控件部分的粘合剂。


    关于 MEF,MEF 给你的是发现和注入扩展代码。它很有价值,但不能直接解决您描述的问题。您的问题与代码的发现关系不大,而与如何组合代码以创建连贯的整体控制的机制有关。 MEF 可以帮助执行一些粘合,但它只是问题的一小部分。

    【讨论】:

    • 这是对 MEF 的深入了解。谢谢,希望这样的评论能帮助我缩小可行的分析途径。
    • @Mystere Man,如果你根本没有玩过 WPF,我建议你看看它。即使您不打算使用它,该框架的设计者也已经融入了很多非常好的想法,并且显然已经在如何创建一个可扩展的 UI 而无需一堆自定义控件代码方面投入了大量精力。如果您使用它,请帮自己一个忙,专注于查看 XAML 而不是使用 VS 设计器。 XAML 是一种极其强大的语言,用于简明描述对象及其关系,直接关系到您的组合问题。
    【解决方案5】:

    你想构建一个插件类型的框架吗?我肯定会向您介绍托管可扩展性框架。可以在带有 WinForms 或 WPF 的 .Net 4.0 中使用。

    【讨论】:

      【解决方案6】:

      如果您对已经实现了 mixin 的框架感兴趣以供参考,请查看 re-motion 框架 (https://www.re-motion.org/)。令人惊讶的是,在这个框架中可以轻松实现 mixins,

      以下是“Hello World”级别的参考应用程序。

      使用从网站下载的程序集,在 VS2010 中创建一个类库和一个控制台应用程序,在两个项目中添加对程序集 remotion.dll 和 remotion.interfaces.dll 的引用。

      using System;
      
      namespace Domain
      {
        public class Person
        {
          public Person()
          {
          }
          public Person(Person person)
          {
            FirstName = person.FirstName;
            LastName = person.LastName;
            BirthDay = person.BirthDay;
          }
      
          public string FirstName { get; set; }
          public string LastName { get; set; }
          public DateTime BirthDay { get; set; }
        }
      }
      
      
      
      using System;
      using Remotion.Mixins;
      
      namespace Domain
      {
        public interface IEmployeeMixin
        {
          int Salary { get; set; }
          DateTime? HireDate { get; set; }
        }
      
        [Extends(typeof(Person))]
        public class EmployeeMixin : IEmployeeMixin
        {
          public int Salary { get; set;  }
          public DateTime? HireDate { get; set;}
          public EmployeeMixin()
          {
            // set default values
            Salary = 1000;
            HireDate = DateTime.Now;
          }
        }
      }
      
      
      
      using Domain;
      using Remotion.Reflection;
      using Remotion.Mixins;
      
      namespace ConsoleApp
      {
        class Program
        {
          static void Main(string[] args)
          {
            Person p = new Person();
            p.FirstName = "John";
            p.LastName = "Doe";
      
            var employee = (IEmployeeMixin)ObjectFactory.Create<Person>(ParamList.Create(p));
      
            System.Console.WriteLine("Fullname: {0}", ((Person)employee).FirstName + " " + ((Person)employee).LastName);
            System.Console.WriteLine("Salary: {0}", employee.Salary);
      
            System.Console.ReadKey();
          }
        }
      }
      

      基于这个参考实现,应该很容易理解这个mixin是如何在这个框架中实现的。如果您想获得有关此框架(它是一个完整的 DDD 开发框架)或 mixins 的更多信息,请不要犹豫。我可以为您提供更多文档。

      斯蒂芬

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-02
        • 2016-09-05
        • 2015-04-23
        相关资源
        最近更新 更多