C# 您将如何重构一个;“接通类型”;如果你不';我们不能控制所涉及的类型吗?

C# 您将如何重构一个;“接通类型”;如果你不';我们不能控制所涉及的类型吗?,c#,generics,oop,reflection,C#,Generics,Oop,Reflection,我正在用C#编写一个解析器,并在寻找一些重构建议。这可能是我在C#中尝试了一段时间的第一个“真正”的项目(在我的日常工作中,我几乎完全用VB6编程),所以我觉得这个问题可能会成为一系列问题中的第一个;-) 让我提供一些到目前为止的背景资料,以便我的问题(希望)有意义 现在,我只有一个类,MicroformatsParser,完成所有的工作。它有一个重载构造函数,允许您传递一个System.Uri或一个包含Uri的string:在构造时,它会在给定的Uri处下载HTML文档,并将其加载到HtmlA

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

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

现在,我只有一个类,
MicroformatsParser
,完成所有的工作。它有一个重载构造函数,允许您传递一个
System.Uri
或一个包含Uri的
string
:在构造时,它会在给定的Uri处下载HTML文档,并将其加载到
HtmlAgilityPack.HtmlDocument
中,以便于类进行操作

基本API的工作原理是这样的(或者,一旦我完成了代码…),它将:

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

  • 检查类型
    T
    是否具有
    ContainerName
    属性(并且该属性不是空的)
  • 在HTML文档中搜索具有与
    ContainerName
    匹配的
    class
    属性的所有节点。将这些称为“容器节点”
  • 对于每个容器节点:
    • 使用反射创建类型为
      T
      的对象
    • 通过反射获取类型
      T
      的公共字段(a
      MemberInfo[]
    • 对于每个字段的
      MemberInfo
      • 如果字段具有
        PropertyName
        属性
        • 从HTML中获取相应微格式属性的值
        • 将在HTML中找到的值注入字段(即在第一步创建的
          T
          类型的对象上设置字段的值)
        • 将类型为
          T
          的对象添加到
          列表中
    • 返回
      列表
      ,该列表现在包含一组微格式对象
我正试图找出一种更好的方法来实施bold中的步骤。问题在于microformat类中给定字段的
类型
,不仅决定了在HTML中查找哪个节点,还决定了如何解释数据

例如,回到上面定义的
HCard
类I,
的“email”
属性绑定到
EmailAddresses
字段,该字段是一个
列表。解析器在HTML中找到父
“vcard”
节点的所有
“email”
子节点后,必须将它们放入
列表中

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

同样的问题也适用于
Float
S、
DateTime
S、
List
S、
List
S等

显而易见的答案是让解析器切换字段的类型,并对每种情况进行适当的转换,但我希望避免出现一个巨大的
switch
语句。请注意,我不打算让解析器支持现有的所有可能的
类型
,但我希望它能够处理大多数标量类型和它们的
列表
版本,以及识别其他微格式类的能力(以便微格式类可以由其他微格式类组成)

关于如何最好地处理这个问题,有什么建议吗

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


我的第一个想法是使用方法重载,所以我会有一系列的
GetPropValue
重载,比如
GetPropValue(HtmlNode节点,ref string retrievedValue)
GetPropValue(HtmlNode,ref List retrievedValue)
,等等。但是我想知道是否有更好的方法来解决这个问题。

您可以构造一个字典,将字符串映射到委托,并在需要使用适当的方法进行解析时进行查找。

Mehrdad的方法基本上就是我建议首先使用的方法,但作为走出这一潜在挑战的第一步

您可以对单个类型(字符串、原语等)使用一个简单的
IDictionary
(其中每个条目实际上是从
T
Func
——但不能用泛型表示),但您还需要检查列表、映射等。您无法使用映射来执行此操作,因为每种类型的列表都必须有一个条目(即,
列表
列表
等的单独条目)。泛型使这变得相当棘手——如果您乐于将自己限制为某些具体类型,例如
List
,您将使自己变得更容易(但灵活性较低)。例如,检测
列表
非常简单:

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
    // Handle lists
    // use type.GetGenericArguments() to work out the element type
}
if(type.IsGenericType&&type.GetGenericTypeDefinition()==typeof(列表))
{
//句柄列表
//使用type.GetGenericArguments()计算元素类型
}
对于某些
T
(然后发现
T
)来说,检测一个类型是否实现了
IList
)可能是一件痛苦的事情,特别是可能有多个实现,具体类型本身可能是泛型的,也可能不是泛型的。如果您真的需要一个由成千上万的开发人员使用的非常灵活的库,那么这项工作可能是值得的,但其他的
[ContainerName("vcard")]
public class HCard
{
    [PropertyName("fn")]
    public string FullName;

    [PropertyName("email")]
    public List<string> EmailAddresses;

    [PropertyName("adr")]
    public List<Address> Addresses;

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