【问题标题】:Understanding MEF System.Lazy<T,TMetaData>了解 MEF System.Lazy<T,TMetaData>
【发布时间】:2014-09-21 02:55:57
【问题描述】:

我一直在研究StockTrader RI for PRISM 的内部工作原理。

在此 RI 中,MEF 和自定义属性系统结合使用以向区域注册视图,而不是在 Module Initializer 中将事物连接到 RegionManager。

更具体地说,有一个ViewExportAttribute 实现:

  1. MetaDataAttribute
  2. IViewRegionRegistration

System.Lazy&lt;T,TMetaData&gt;AutoPopulateExportedViewsBehavior 中可以利用 MetaDataAttribute 和“属性视图”IViewRegionRegistration 来实现区域和视图的正确链接。

一般System.Lazy&lt;T,TMetaData&gt; 和实际元数据之间的相互作用在here 中进行了详细说明,更具体地说是“使用强类型元数据”部分。

明确地说,我理解 Lazy 的意图,它显然有效。但是,我完全不明白的是属性提供的元数据视图(它只是一个接口)和用 MetaDataAttribute 提供的实际数据填充 TMetaData 属性之间的链接发生位置和方式 .

为了让我的要求更清楚,来自previously referenced example

首先,定义一个接口,它可以作为一种模板来传递某些元数据:

public interface IMessageSenderCapabilities
{
    MessageTransport Transport { get; }
    bool IsSecure { get; }
}

接下来,定义一个对应的MetaDataAttribute(与上一个接口的属性相同)

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MessageSenderAttribute : ExportAttribute
{
    public MessageSenderAttribute() : base(typeof(IMessageSender)) { }
    public MessageTransport Transport { get; set; }
    public bool IsSecure { get; set; }
}

该属性可用于导出,其中为属性属性设置实际值:

[MessageSender(Transport=MessageTransport.Smtp, IsSecure=true)]
public class SecureEmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine(message);
    }
}

现在终于可以进行一些导入了:

public class HttpServerHealthMonitor
{
    [ImportMany]
    public Lazy<IMessageSender, IMessageSenderCapabilities>[] Senders { get; set; }

    public void SendNotification()
    {
        foreach(var sender in Senders)
        {
            if (sender.Metadata.Transport == MessageTransport.Smtp && 
                sender.Metadata.IsSecure)
            {
                var messageSender = sender.Value;
                messageSender.Send("Server is fine");

                break;
            }
        }
    }
}

在这最后一步中:sender.Metadata.Transport 在那个非常 Lazy 上进行评估。因此,在此过程中,Lazy 会意识到元数据的实际值,而不仅仅是它传递的接口。 我想了解这是如何发生的,谁或什么要对此负责。即使它只是一个非常一般的流程。

【问题讨论】:

    标签: c# mvvm prism mef


    【解决方案1】:

    经过更多反射器后,我想我可以开始制定答案,尽管事实证明很多事情正在发生,所以这个答案可能会演变。我把它写下来听听,以便我自己学习。

    1. MEFBootsrapper.Run()

      ...

    2. MEFBootstrapper.Container.GetExports(...) 因为CompositionContainer : ExportProvider, ... 和 ExportProvider 定义了public Lazy&lt;T, TMetadataView&gt; GetExport&lt;T, TMetadataView&gt;()

    3. 下一个private Lazy&lt;T, TMetadataView&gt; GetExportCore&lt;T, TMetadataView&gt;(string contractName)

    4. 下一个internal static Lazy&lt;T, M&gt; CreateStronglyTypedLazyOfTM&lt;T, M&gt;(Export export)

    5. 在这里,AttributedModelServices.GetMetadataView&lt;M&gt;(export.Metadata) 其中 M 是 MetaDataView 的类型。而 export 本身是 System.ComponentModel.Composition.Primitives.Export 类型,并且它有一个字段 ExportDefenition,其中存在继承的 AttributedExportDefenition

    6. AttributedExportDefenition.MetaData 的 getter 包含 this._member.TryExportMetadataForMember(out strs);

    7. TryExportMetadataForMember(...) 终于有一个检查 type.IsAttributeDefined&lt;MetadataAttributeAttribute&gt; 看看是否有一个 MetadataAttribute 应用,例如问题中的MessageSenderAttribute

    所以这或多或少(非常粗略地)是我们如何在导出时获得实际元数据,因此可能会走更多弯路,这些导出的元数据也将到达 Lazy,尽管我仍然想知道它是如何工作的.

    任何反馈仍将不胜感激。

    【讨论】:

      【解决方案2】:

      试图了解我最初问题中的代码发生了什么,引发了另一个问题:

      StockTrader RI 与MEF Documentation 中提供的示例之间存在细微差别

      在 Stocktrader 中,ViewExportAttribute 被定义:

       [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
       [MetadataAttribute]
       public sealed class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
       {
          ... omitted for brevity ...
       }
      

      MEF 文档给出了一个类似的例子(也在原始问题中):

      [MetadataAttribute]
      [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
      public class MessageSenderAttribute : ExportAttribute
      {
          public MessageSenderAttribute() : base(typeof(IMessageSender)) { }
          public MessageTransport Transport { get; set; }
          public bool IsSecure { get; set; }
      }
      

      因此,对于上述代码块,不同之处在于,在第一种情况下,属性派生自定义“元数据视图”的接口,而在第二个示例中,情况并非如此;该属性只是具有与IMessageSenderCapabilities 接口相同的属性。

      您会认为“没什么大不了”,但在 StockTrader RI 中:

      [ImportMany(AllowRecomposition = true)]
      public Lazy<object, IViewRegionRegistration>[] RegisteredViews { get; set; }
      

      而在 MEF 示例中:

      [ImportMany]
      public Lazy<IMessageSender, IMessageSenderCapabilities>[] Senders { get; set; }
      

      因此,这里的区别在于,在 Stocktrader RI 中,我们尝试延迟导入的类型未指定(它只是对象),而在第二个中,它被更具体地定义(IMessageSender)。

      最终结果或多或少是一样的,一些类型是懒惰地与元数据一起导入的。

      不过,我还想学习的是:

      1. 如果两个示例中各个关键点的差异相关。
      2. 特别是在股票交易者示例中,我们如何知道作为 Lazy 导入什么?是不是因为ViewExportAttribute 专门派生自IViewRegionRegistration,所以我们以后可以拥有Lazy&lt;object, ...,即系统知道要导入什么,因为只有具有该元数据的类型才会被导入?所有这一切都没有指定 object 实际上是视图,即 UserControls?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多