【问题标题】:Injecting properties into .NET classes post-compile编译后将属性注入 .NET 类
【发布时间】:2009-03-24 15:47:40
【问题描述】:

我想在不引用 WPF 程序集的情况下实现 WPF 的 MVVM 模式的 ViewModel 部分。有问题的部分是命令路由,它要求 ViewModel 实现 ICommand 类型的属性,以便命令绑定可以工作。

现在,我可以避免使用ICommand,只需将属性声明为object。一切仍然有效,就是这样。但困扰我的是,我仍然必须声明它们,我真的不想这样做,因为它们感觉就像样板代码

我的 ViewModel 目前如下所示:

public class HelloWorldViewModel : ViewModel
{
    [BoundProperty]
    public string Name { get; set; }

    [CommandHandler("SayHello")]
    public bool CanSayHello()
    {
        return Name != "" && Name != null;
    }

    [CommandHandler("SayHello")]
    public void SayHello()
    {
        View.ShowMessage("Hello, {0}!", Name);
    }

    public object SayHello { get; private set; }
}

CommandHandlerAttribute 支持命令处理程序的运行时发现(Action 和可选的Func<bool>),而BoundPropertyAttribute 确实是一个方面,它将自身注入到属性设置器和致电INotifyPropertyChanged。我通过使用编译时 IL 编织器来实现这一点。

理想情况下,我也想隐含最后一行(SayHello 属性)。如果不是 WPF 的要求,那么将它放在源中是没有意义的。

所以,很自然地,我正在考虑使用CommandHandlerAttribute 方面将必要的IL 注入到类中,并且本质上是在编译后创建属性。这相当困难,尽管一个好的 IL 编织器(例如 PostSharp)可以大大简化它。

在我踏上这段旅程之前,我想听听大家对我的方法的看法。好听吗?有没有更好的办法?你会/怎么做?

【问题讨论】:

    标签: c# wpf mvvm postsharp code-injection


    【解决方案1】:

    在我看来,这听起来太聪明了。发生了太多的“魔术”。特别是,我不喜欢您的 CommandHandlerAttribute 的魔术字符串和其他方面。也就是说,如果我要走这条路,我会使用类似于 EventAggregator 的东西,但用于命令。 IOW,SayHello 根本不存在于您的 ViewModel 上。任何魔术都会创建与 SayHell() 和 CanSayHello() 的命令绑定,而是将命令定位在全局 CommandAggregator 中。只要我们为此使用魔术字符串,CommandAggregator 中的命令就可以被延迟创建,因此您不需要“样板”编码。剩下的就是创建一些 XAML 魔法(标记扩展)来指定 ICommandSource 上的命令。

    <Button Command="{my:AggregateCommand SayHello}"/>
    

    【讨论】:

      【解决方案2】:

      我建议您看看这是如何实现的,这将有所帮助:

      “一种魔法” 轻松 INotifyPropertyChanged

      [http://visualstudiogallery.msdn.microsoft.com/d5cd6aa1-57a5-4aaa-a2be-969c6db7f88a][1]

      作为将其添加到一个属性的示例:

      [Magic] 
      public string Name { get { return _name; } set { _name = value; } } 
      string _name;
      

      将它添加到所有类属性的另一个示例:

      [Magic] 
      public class MyViewModel: INotifyPropertyChanged 
      { 
        public string Name { get; set; } 
        public string LastName { get; set; } 
        ..... 
      }
      

      【讨论】:

        【解决方案3】:

        在玩了 Prism 一段时间后,但在我看起来像 MVVM 之前,我想出了一个我仍然认为有一定道理的策略:

        我创建了一个基于反射的 ICommand 接口的实现。构造函数接受一个目标对象和一个操作名称。使用反射,代码查找名称为“[operation]”的方法、名称为“Can[operation]”或“[operation]Enabled”的属性或方法以及名称为“Can[operation]Changed”或“[操作]启用更改”。只有第一个是必需的,但反射的方法/属性/事件被连接到 ICommand 接口的一个非常基本的实现。

        然后我创建了 IValueConverter 的实现,它将创建前一个类的实例,将要转换的值作为目标对象传递,转换器的参数是操作名称。

        鉴于上述组件,然后我可以将按钮的 Command 属性直接绑定到操作的源(以及指定转换器),并将按钮的 CommandParameter 属性设置为操作的名称。通过这种方式,我得到了声明式命令绑定,而命令源没有任何 WPF 知识。

        【讨论】:

        • 我不喜欢使用带有“魔法”方法和属性名称的反射。我过去经常发现,我重命名了一个方法,然后意外的一些 UI 停止工作,而编译器没有给我一个错误。不过我喜欢你的基本设计,那 lambda 表达式来做连线呢?
        • 我非常喜欢 lambda 的想法。我可能仍然更喜欢通过反射(至少默认情况下)发现“启用”属性/事件,因为它会简化界面。
        【解决方案4】:

        我个人认为这很有趣,但我一般会避免它。

        避免样板代码(或感觉像样板代码的代码)会产生后果。这似乎是个好主意,因为您不会不断地重新输入内容,但从长远来看,您会降低它的可读性和可理解性。

        就个人而言,我尝试设置好的代码模板来为我插入样板代码,并将其包装在区域中,以便我可以将其隐藏在源代码中。在这种情况下,用样板文件填写文件所需的 30 秒(对我而言)比我花费的 2 小时(两年后我试图理解代码,或者更糟糕的是,两周后某人)更痛苦else 两年后他们试图理解我的代码....

        【讨论】:

          【解决方案5】:

          我认为最好的方法是代理或装饰器模式。您可以在运行时使用 UI/WPF 成员包装/装饰的低级实体。这是最简单但有效的方法,可以节省您的时间,并且无需为框架、注入等烦恼。

          唯一的事情是您必须设计一些小型基础设施来用适当的装饰器包装您的实体。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-02-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-10-16
            • 1970-01-01
            • 1970-01-01
            • 2019-09-01
            相关资源
            最近更新 更多