使用ImpromptuInterface反射库方便的创建自定义DfaGraphWriter

栏目: IT技术 · 发布时间: 3年前

内容简介:在本文中,我为创建的自定义的作者:依乐祝原文地址:

在本文中,我为创建的自定义的 DfaGraphWriter 实现奠定了基础。 DfaGraphWriter 是公开的,因此您可以如上一篇文章中所示在应用程序中使用它,但它 使用的 所有类均已标记为 internal 。这使得创建自己的版本成为问题。要解决此问题,我使用了 一个开源的反射库 ImpromptuInterface ,使创建自定义的 DfaGraphWriter 实现更加容易。

作者:依乐祝

原文地址: https://andrewlock.net/creating-a-custom-dfagraphwriter-using-impromptuinterface-and reflection/

译文地址: https://www.cnblogs.com/yilezhu/p/13336066.html

我们将从查看现有的 DfaGraphWriter 开始,以了解其使用的 internal 类以及导致我们的问题。然后,我们来看一下使用一些自定义接口和 ImpromptuInterface 库来允许我们调用这些类。在下一篇文章中,我们将研究如何使用自定义界面创建的自定义版本 DfaGraphWriter

探索现有的 DfaGraphWriter

DfaGraphWriter 类是存在于ASP.NET Core中的一个“pubternal”文件夹 中的。它已注册为单例,并使用注入的 IServiceProvider 来解析 DfaMatcherBuilder

public class DfaGraphWriter
{
    private readonly IServiceProvider _services;
    public DfaGraphWriter(IServiceProvider services)
    {
        _services = services;
    }

    public void Write(EndpointDataSource dataSource, TextWriter writer)
    {
        // retrieve the required DfaMatcherBuilder
        var builder = _services.GetRequiredService<DfaMatcherBuilder>();

        // loop through the endpoints in the dataSource, and add them to the builder
        var endpoints = dataSource.Endpoints;
        for (var i = 0; i < endpoints.Count; i++)
        {
            if (endpoints[i] is RouteEndpoint endpoint && (endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching ?? false) == false)
            {
                builder.AddEndpoint(endpoint);
            }
        }

        // Build the DfaTree. 
        // This is what we use to create the endpoint graph
        var tree = builder.BuildDfaTree(includeLabel: true);

        // Add the header
        writer.WriteLine("digraph DFA {");

        // Visit each node in the graph to create the output
        tree.Visit(WriteNode);

        //Close the graph
        writer.WriteLine("}");

        // Recursively walks the tree, writing it to the TextWriter
        void WriteNode(DfaNode node)
        {
            // Removed for brevity - we'll explore it in the next post
        }
    }
}

上面的代码显示了图形编写者 Write 方法的所有操作,终结如下:

  • 获取一个 DfaMatcherBuilder
  • 写入所有的端点 EndpointDataSourceDfaMatcherBuilder
  • 调用 DfaMatcherBuilderBuildDfaTree 。这将创建一个 DfaNode 的 图。
  • 访问 DfaNode 树中的每一个,并将其写入 TextWriter 输出。我们将在下一篇文章中探讨这种方法。

创建我们自己的自定义编写器的目的是通过控制如何将不同的节点写入输出来定制最后一步,因此我们可以创建更多的描述性的图形,如我先前所示:

使用ImpromptuInterface反射库方便的创建自定义DfaGraphWriter

我们的问题是两个重点类, DfaMatcherBuilderDfaNode ,是 internal 所以我们不能轻易实例化它们,或者使用它们的写入方法。这给出了两个选择:

  • 重新实现这些 internal 类,包括 它们 依赖的其他任何 internal 类。
  • 使用反射在现有类上创建和调用方法。

这些都不是很好的选择,但是鉴于端点图不是性能关键的东西,我决定使用反射将是最简单的。为了使事情变得更加简单,我使用了开源库 ImpromptuInterface

ImpromptuInterface使反射更容易

ImpromptuInterface 是一个库它使调用动态对象或调用存储在对象引用中的底层对象上的方法变得更加容易。它本质上增加了简单的duck/structural类型,允许您为对象使用stronlgy类型化接口。它使用 Dynamic Language Runtime Reflection.Emit 来实现。

例如,让我们获取我们要使用的现有 DfaMatcherBuilder 类。即使我们不能直接引用它,我们仍然可以从DI容器中获取此类的实例,如下所示:

// get the DfaMatcherBuilder type - internal, so needs reflection :(
Type matcherBuilder = typeof(IEndpointSelectorPolicy).Assembly
    .GetType("Microsoft.AspNetCore.Routing.Matching.DfaMatcherBuilder");

object rawBuilder = _services.GetRequiredService(matcherBuilder);

rawBuilder 是一个 object 引用,但它 包含 了一个 DfaMatcherBuilder 的实例。我们不能直接在调用它的方法,但是我们可以通过直接构建 MethodInfo 和直接调用 invoke 来使用反射来调用它们。。

ImpromptuInterface 通过提供一个可以 直接 调用方法的静态接口,使该过程更加容易。例如,对于 DfaMatcherBuilder ,我们只需要调用两个方法 AddEndpointBuildDfaTree 。原始类如下所示:

internal class DfaMatcherBuilder : MatcherBuilder
{
    public override void AddEndpoint(RouteEndpoint endpoint) { /* body */ }
    public DfaNode BuildDfaTree(bool includeLabel = false)
}

我们可以创建一个暴露这些方法的接口:

public interface IDfaMatcherBuilder
{
    void AddEndpoint(RouteEndpoint endpoint);
    object BuildDfaTree(bool includeLabel = false);
}

然后,我们可以使用 ImpromptuInterface ActLike<> 方法创建实现了 IDfaMatcherBuilder 的代理对象。此代理包装 rawbuilder 对象,因此当您在接口上调用方法时,它将在底层调用 DfaMatcherBuilder 中的等效的方法:

使用ImpromptuInterface反射库方便的创建自定义DfaGraphWriter

在代码中,如下所示:

// An instance of DfaMatcherBuilder in an object reference
object rawBuilder = _services.GetRequiredService(matcherBuilder);

// wrap the instance in the ImpromptuInterface interface
IDfaMatcherBuilder builder = rawBuilder.ActLike<IDfaMatcherBuilder>();

// we can now call methods on the builder directly, e.g. 
object rawTree =  builder.BuildDfaTree();

原始 DfaMatcherBuilder.BuildDfaTree() 方法和接口版本之间有一个重要区别:原始方法返回一个 DfaNode ,但这是另一个 internal 类,因此我们无法在接口中引用它。

相反,我们为 DfaNode 创建另一个 ImpromptuInterface ,暴露我们将需要的属性(在接下来的文章中你就会明白为什么我们需要他们):

public interface IDfaNode
{
    public string Label { get; set; }
    public List<Endpoint> Matches { get; }
    public IDictionary Literals { get; } // actually a Dictionary<string, DfaNode>
    public object Parameters { get; } // actually a DfaNode
    public object CatchAll { get; } // actually a DfaNode
    public IDictionary PolicyEdges { get; } // actually a Dictionary<object, DfaNode>
}

在下一篇文章中,我们将在 WriteNode 的方法中使用这些属性,但是有一些复杂性。在原始 DfaNode 类中, ParametersCatchAll 属性返回 DfaNode 对象。在我们 IDfaNode 版本的属性中,我们必须返回 object 。我们无法引用 DfaNode (因为是 internal )并且我们不能返回 IDfaNode ,因为 DfaNode 它没有 实现 IDfaNode ,因此您不能将 object 引用隐式转换为 IDfaNode 。你必须使用 ImpromptuInterface显式地 添加一个实现了接口的代理,。

例如:

// Wrap the instance in the ImpromptuInterface interface
IDfaMatcherBuilder builder = rawBuilder.ActLike<IDfaMatcherBuilder>();

// We can now call methods on the builder directly, e.g. 
object rawTree =  builder.BuildDfaTree();
// Use ImpromptuInterface to add an IDfaNode wrapper
IDfaNode tree = rawTree.ActLike<IDfaNode>();

// We can now call methods and properties on the node...
object rawParameters = tree.Parameters;
// ...but they need to be wrapped using ImpromptuInterface too
IDfaNode parameters = rawParameters.ActLike<IDfaNode>();

返回 Dictionary 类型的属性还有另一个问题: LiteralsPolicyEdges 。实际返回的类型分别为 Dictionary<string, DfaNode>Dictionary<object, DfaNode> ,但是我们需要使用一个 包含该 DfaNode 类型的类型。不幸的是,这意味着我们不得不退回到.NET 1.1 IDictionary 接口!

您不能将一个 Dictionary<string, DfaNode> 强制转换为 IDictionary<string, object> ,因为这样做 将是不安全的协方差形式

IDictionary 是一个非泛型接口,因此 keyvalue 仅作为 object 公开。对于 string 键,您可以直接进行转换,对于, DfaNode 我们可以使用 ImpromptuInterface 为我们创建代理包装器:

// Enumerate the key-value pairs as DictinoaryEntrys
foreach (DictionaryEntry dictEntry in node.Literals)
{
    // Cast the key value to a string directly
    var key = (string)dictEntry.Key;
    // Use ImpromptuInterface to add a wrapper
    IDfaNode value = dictEntry.Value.ActLike<IDfaNode>();
}

现在,我们已经拥有了通过实现 WriteNode 来创建自定义 DfaWriter 实现所需的一切对象,但是这篇文章已经有点长了,所以我们将在下一篇文章中探讨如何实现这一点!

摘要

在本文中,我探讨了 DfaWriter 在ASP.NET Core 中的实现以及它使用的两个 internal 类: DfaMatcherBuilderDfaNode 。这些类是内部类的事实使得创建我们自己的 DfaWriter 实现非常棘手。为了干净地实现它,我们将不得不重新实现这两种类型以及 它们所 依赖的所有类。

作为替代,我使用 ImpromptuInterface 库创建了一个包装器代理,该代理实现与被包装的对象拥有类似的方法。这使用反射来调用包装属性上的方法,但允许我们使用强类型接口。在下一篇文章中,我将展示如何使用这些包装器创建一个定制的 DfaWriter 来进行端点图的自定义。


以上所述就是小编给大家介绍的《使用ImpromptuInterface反射库方便的创建自定义DfaGraphWriter》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java Web服务:构建与运行

Java Web服务:构建与运行

Martin Kalin / 任增刚 / 电子工业出版社 / 2009年11月 / 45.00元

本书以示例驱动的方式详尽地介绍了XML Web服务(JAX-WS)和RESTful Web服务(JAX-RS)二者所涵盖的Java相关API。 《Java Web服务:构建和运行》这本书以清晰、务实的方法讲述Web服务相关技术,提供了混合性的架构总结、完全可以运行的代码示例,以及编译、部署和执行应用程序的一些短小精悍的指令。学习本书,读者将掌握如何从零开始编写Web服务或将已有的服务整合到现......一起来看看 《Java Web服务:构建与运行》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具