内容简介:在本文中,我为创建的自定义的作者:依乐祝原文地址:
在本文中,我为创建的自定义的 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
- 写入所有的端点
EndpointDataSource
到DfaMatcherBuilder
。 - 调用
DfaMatcherBuilder
的BuildDfaTree
。这将创建一个DfaNode
的 图。 - 访问
DfaNode
树中的每一个,并将其写入TextWriter
输出。我们将在下一篇文章中探讨这种方法。
创建我们自己的自定义编写器的目的是通过控制如何将不同的节点写入输出来定制最后一步,因此我们可以创建更多的描述性的图形,如我先前所示:
我们的问题是两个重点类, DfaMatcherBuilder
和 DfaNode
,是 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
,我们只需要调用两个方法 AddEndpoint
和 BuildDfaTree
。原始类如下所示:
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
中的等效的方法:
在代码中,如下所示:
// 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
类中, Parameters
和 CatchAll
属性返回 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
类型的属性还有另一个问题: Literals
和 PolicyEdges
。实际返回的类型分别为 Dictionary<string, DfaNode>
和 Dictionary<object, DfaNode>
,但是我们需要使用一个 不 包含该 DfaNode
类型的类型。不幸的是,这意味着我们不得不退回到.NET 1.1 IDictionary
接口!
您不能将一个 Dictionary<string, DfaNode>
强制转换为 IDictionary<string, object>
,因为这样做 将是不安全的协方差形式 。
IDictionary
是一个非泛型接口,因此 key
和 value
仅作为 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
类: DfaMatcherBuilder
和 DfaNode
。这些类是内部类的事实使得创建我们自己的 DfaWriter
实现非常棘手。为了干净地实现它,我们将不得不重新实现这两种类型以及 它们所 依赖的所有类。
作为替代,我使用 ImpromptuInterface 库创建了一个包装器代理,该代理实现与被包装的对象拥有类似的方法。这使用反射来调用包装属性上的方法,但允许我们使用强类型接口。在下一篇文章中,我将展示如何使用这些包装器创建一个定制的 DfaWriter
来进行端点图的自定义。
以上所述就是小编给大家介绍的《使用ImpromptuInterface反射库方便的创建自定义DfaGraphWriter》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Go语言反射之反射调用
- Go语言反射之类型反射
- Go语言反射之值反射
- 模块讲解----反射 (基于web路由的反射)
- 装饰器与元数据反射(4)元数据反射
- .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法导论(原书第2版)
[美] Thomas H.Cormen、Charles E.Leiserson、Ronald L.Rivest、Clifford Stein / 潘金贵 等 / 机械工业出版社 / 2006-9 / 85.00元
这本书深入浅出,全面地介绍了计算机算法。对每一个算法的分析既易于理解又十分有趣,并保持了数学严谨性。本书的设计目标全面,适用于多种用途。涵盖的内容有:算法在计算中的作用,概率分析和随机算法的介绍。书中专门讨论了线性规划,介绍了动态规划的两个应用,随机化和线性规划技术的近似算法等,还有有关递归求解、快速排序中用到的划分方法与期望线性时间顺序统计算法,以及对贪心算法元素的讨论。此书还介绍了对强连通子图......一起来看看 《算法导论(原书第2版)》 这本书的介绍吧!