Mik*_*oss 8 c# oop generics reflection
我正在用C#编写一个微格式解析器,我正在寻找一些重构建议.这可能是我在C#中尝试了一段时间的第一个"真正的"项目(我在日常工作中几乎完全用VB6编程),所以我觉得这个问题可能成为系列中的第一个;-)
让我提供一些关于我到目前为止的背景,以便我的问题(希望)有意义.
现在,我有一个班级,完成MicroformatsParser所有工作.它有一个重载的构造函数,允许你传递System.Uri或string包含一个URI:在构造时,它会在给定的URI下载HTML文档并将其加载到一个HtmlAgilityPack.HtmlDocument容易操作的类中.
基本API就像这样(或者,一旦我完成代码......):
MicroformatsParser mp = new MicroformatsParser("http://microformats.org");
List<HCard> hcards = mp.GetAll<HCard>();
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);
}
Run Code Online (Sandbox Code Playgroud)
这里使用泛型是有意的.我从Firefox 3中的Microformats库(以及Ruby mofogem)的方式中获得灵感.这里的想法是解析器执行繁重的工作(在HTML中查找实际的微格式内容),微格式类本身(HCard在上面的示例中)基本上提供了告诉解析器如何处理它找到的数据的模式.
HCard该类的代码应该更清楚(注意这不是一个完整的实现):
[ContainerName("vcard")]
public class HCard
{
[PropertyName("fn")]
public string FullName;
[PropertyName("email")]
public List<string> EmailAddresses;
[PropertyName("adr")]
public List<Address> Addresses;
public HCard()
{
}
}
Run Code Online (Sandbox Code Playgroud)
解析器使用此处的属性来确定如何使用HTML文档中的数据填充类的实例.调用时,解析器执行以下操作GetAll<T>():
T是否具有ContainerName属性(并且它不是空白)class匹配的属性的所有节点ContainerName.将这些称为"容器节点".T.MemberInfo[])的类型TMemberInfo
PropertyName属性
T在第一步中创建的类型对象上设置字段的值)T到aList<T>List<T>,现在包含一堆微格式对象我正试图找出一种更好的方法来实现粗体步骤.问题是,Type微格式类中的给定字段不仅决定了HTML中要查找的节点,还决定了如何解释数据.
例如,回到HCard上面定义的类,"email"属性绑定到EmailAddresses字段,即a List<string>.解析器在HTML中找到"email"父"vcard"节点的所有子节点后,必须将它们放在一个List<string>.
更重要的是,如果我希望我HCard能够返回电话号码信息,我可能希望能够声明一个新的类型字段List<HCard.TelephoneNumber>(具有自己的ContainerName("tel")属性)来保存该信息,因为可以有多个"tel"元素HTML,"tel"格式有自己的子属性.但现在解析器需要知道如何将电话数据放入List<HCard.TelephoneNumber>.
同样的问题适用于FloatS,DateTimeS,List<Float>S,List<Integer>S等.
显而易见的答案是让解析器切换到字段类型,并为每种情况做适当的转换,但我想避免一个巨大的switch声明.请注意,我并不打算让解析器支持所有可能Type存在的,但我希望它能处理大多数标量类型及其List<T>版本,以及识别其他微格式类的能力(以便微格式类可以由其他微格式类组成).
关于如何最好地处理这个的任何建议?
由于解析器必须处理原始数据类型,我认为我不能在类型级别添加多态性...
我首先想到的是使用方法重载,所以我想有一个系列的GetPropValue像重载GetPropValue(HtmlNode node, ref string retrievedValue),GetPropValue(HtmlNode, ref List<Float> retrievedValue)等等.但我不知道是否有解决这个问题的更好方法.
Mehrdad的方法基本上是我建议的方法,但作为可能更多的第一步.
您可以使用一个简单的IDictionary<Type,Delegate>(其中每个条目实际上是从T到Func<ParseContext,T>-但不能用泛型来表示),单类型(字符串,基元等),但是你还需要检查列表,地图等你赢"T能够做到这一点使用的地图,因为你必须为每个类型列表中(即单独的条目的条目List<string>,List<int>等等).泛型使这非常棘手 - 如果你很乐意将自己局限于某些具体类型,比如List<T>你会让自己更容易(但不那么灵活).例如,检测List<T>很简单:
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
// Handle lists
// use type.GetGenericArguments() to work out the element type
}
Run Code Online (Sandbox Code Playgroud)
检测类型是否IList<T>为某些T(然后发现T)实现可能是一种痛苦,特别是因为可能存在多个实现,并且具体类型本身可能是也可能不是通用的.如果您真的需要成千上万开发人员使用的非常灵活的库,那么这项工作可能是值得的 - 但我会保持简单.