当前位置:  开发笔记 > 编程语言 > 正文

如果你不控制所涉及的类型,你如何重构"打开类型"是多态的?

如何解决《如果你不控制所涉及的类型,你如何重构"打开类型"是多态的?》经验,为你挑选了1个好方法。

我正在用C#编写一个微格式解析器,我正在寻找一些重构建议.这可能是我在C#中尝试了一段时间的第一个"真正的"项目(我在日常工作中几乎完全用VB6编程),所以我觉得这个问题可能成为系列中的第一个;-)

让我提供一些关于我到目前为止的背景,以便我的问题(希望)有意义.

现在,我有一个班级,完成MicroformatsParser所有工作.它有一个重载的构造函数,允许你传递System.Uristring包含一个URI:在构造时,它会在给定的URI下载HTML文档并将其加载到一个HtmlAgilityPack.HtmlDocument容易操作的类中.

基本API就像这样(或者,一旦我完成代码......):

MicroformatsParser mp = new MicroformatsParser("http://microformats.org");
List hcards = mp.GetAll();

foreach(HCard hcard in hcards) 
{
    Console.WriteLine("Full Name: {0}", hcard.FullName);

    foreach(string email in hcard.EmailAddresses)
        Console.WriteLine("E-Mail Address: {0}", email);
}

这里使用泛型是有意的.我从Firefox 3中的Microformats库(以及Ruby mofogem)的方式中获得灵感.这里的想法是解析器执行繁重的工作(在HTML中查找实际的微格式内容),微格式类本身(HCard在上面的示例中)基本上提供了告诉解析器如何处理它找到的数据的模式.

HCard该类的代码应该更清楚(注意这不是一个完整的实现):

[ContainerName("vcard")]
public class HCard
{
    [PropertyName("fn")]
    public string FullName;

    [PropertyName("email")]
    public List EmailAddresses;

    [PropertyName("adr")]
    public List
Addresses; public HCard() { } }

解析器使用此处的属性来确定如何使用HTML文档中的数据填充类的实例.调用时,解析器执行以下操作GetAll():

检查类型T是否具有ContainerName属性(并且它不是空白)

在HTML文档中搜索具有与之class匹配的属性的所有节点ContainerName.将这些称为"容器节点".

对于每个容器节点:

使用反射创建类型的对象T.

通过反射获取公共字段(a MemberInfo[])的类型T

对于每个领域 MemberInfo

如果该字段具有PropertyName属性

从HTML获取相应微格式属性的值

将HTML中找到的值注入字段(即T在第一步中创建的类型对象上设置字段的值)

将类型的对象添加T到aList

返回List,现在包含一堆微格式对象

我正试图找出一种更好的方法来实现粗体步骤.问题是,Type微格式类中的给定字段不仅决定了HTML中要查找的节点,还决定了如何解释数据.

例如,回到HCard上面定义的类,"email"属性绑定到EmailAddresses字段,即a List.解析器在HTML中找到"email""vcard"节点的所有子节点后,必须将它们放在一个List.

更重要的是,如果我希望我HCard能够返回电话号码信息,我可能希望能够声明一个新的类型字段List(具有自己的ContainerName("tel")属性)来保存该信息,因为可以有多个"tel"元素HTML,"tel"格式有自己的子属性.但现在解析器需要知道如何将电话数据放入List.

同样的问题适用于FloatS,DateTimeS,ListS,ListS等.

显而易见的答案是让解析器切换到字段类型,并为每种情况做适当的转换,但我想避免一个巨大的switch声明.请注意,我并不打算让解析器支持所有可能Type存在的,但我希望它能处理大多数标量类型及其List版本,以及识别其他微格式类的能力(以便微格式类可以由其他微格式类组成).

关于如何最好地处理这个的任何建议?

由于解析器必须处理原始数据类型,我认为我不能在类型级别添加多态性...

我首先想到的是使用方法重载,所以我想有一个系列的GetPropValue像重载GetPropValue(HtmlNode node, ref string retrievedValue),GetPropValue(HtmlNode, ref List retrievedValue)等等.但我不知道是否有解决这个问题的更好方法.



1> Jon Skeet..:

Mehrdad的方法基本上是我建议的方法,但作为可能更多的第一步.

您可以使用一个简单的IDictionary(其中每个条目实际上是从TFunc-但不能用泛型来表示),单类型(字符串,基元等),但是你还需要检查列表,地图等你赢"T能够做到这一点使用的地图,因为你必须为每个类型列表中(即单独的条目的条目List,List等等).泛型使这非常棘手 - 如果你很乐意将自己局限于某些具体类型,比如List你会让自己更容易(但不那么灵活).例如,检测List很简单:

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
    // Handle lists
    // use type.GetGenericArguments() to work out the element type
}

检测类型是否IList为某些T(然后发现T)实现可能是一种痛苦,特别是因为可能存在多个实现,并且具体类型本身可能是也可能不是通用的.如果您真的需要成千上万开发人员使用的非常灵活的库,那么这项工作可能是值得的 - 但我会保持简单.

推荐阅读
ifx0448363
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有