C# 您将如何重构一个;“接通类型”;如果你不';我们不能控制所涉及的类型吗?
我正在用C#编写一个解析器,并在寻找一些重构建议。这可能是我在C#中尝试了一段时间的第一个“真正”的项目(在我的日常工作中,我几乎完全用VB6编程),所以我觉得这个问题可能会成为一系列问题中的第一个;-) 让我提供一些到目前为止的背景资料,以便我的问题(希望)有意义 现在,我只有一个类,C# 您将如何重构一个;“接通类型”;如果你不';我们不能控制所涉及的类型吗?,c#,generics,oop,reflection,C#,Generics,Oop,Reflection,我正在用C#编写一个解析器,并在寻找一些重构建议。这可能是我在C#中尝试了一段时间的第一个“真正”的项目(在我的日常工作中,我几乎完全用VB6编程),所以我觉得这个问题可能会成为一系列问题中的第一个;-) 让我提供一些到目前为止的背景资料,以便我的问题(希望)有意义 现在,我只有一个类,MicroformatsParser,完成所有的工作。它有一个重载构造函数,允许您传递一个System.Uri或一个包含Uri的string:在构造时,它会在给定的Uri处下载HTML文档,并将其加载到HtmlA
MicroformatsParser
,完成所有的工作。它有一个重载构造函数,允许您传递一个System.Uri
或一个包含Uri的string
:在构造时,它会在给定的Uri处下载HTML文档,并将其加载到HtmlAgilityPack.HtmlDocument
中,以便于类进行操作
基本API的工作原理是这样的(或者,一旦我完成了代码…),它将:
解析器使用这里的属性来确定如何使用HTML文档中的数据填充类的实例。调用GetAll()
时,解析器执行以下操作:
- 检查类型
是否具有T
属性(并且该属性不是空的)ContainerName
- 在HTML文档中搜索具有与
匹配的ContainerName
属性的所有节点。将这些称为“容器节点”class
- 对于每个容器节点:
- 使用反射创建类型为
的对象T
- 通过反射获取类型
的公共字段(aT
)MemberInfo[]
- 对于每个字段的
MemberInfo
- 如果字段具有
属性PropertyName
- 从HTML中获取相应微格式属性的值
- 将在HTML中找到的值注入字段(即在第一步创建的
类型的对象上设置字段的值)T
- 将类型为
的对象添加到T
列表中
- 如果字段具有
- 返回
,该列表现在包含一组微格式对象列表
- 使用反射创建类型为
类型
,不仅决定了在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
}