2012-07-09 43 views
4

我们有一个文件格式,我们需要分析,看起来像:序列属性在C#类

v1|000|sammy|endpoint|blah 

这是一个有序的固定宽度格式的供应商提供给我们,所以每个那些5个的地图到类中的特定属性(实际格式大于30)。

我想通过对属性应用顺序来解析这个问题。我可以做的一件事就是自己创建一些东西 - 编写一个采用单个数字的Attribute类,并将该属性应用到每个属性及其序列索引,并在OrderBy子句中的Reflection中查找它。

在C#中有现成的或更好的方法吗?例如,这是否已经有一个属性?有没有办法在C#或甚至MSIL中询问在一个类中声明了哪些订单属性?

+0

这取决于。有许多方法,但它们不能保证可以工作,它们依赖于实现细节,这些细节可能会在更新版本的编译器和更新版本的.NET Framework中发生变化。你想要一个解决方案有多可靠? – hvd 2012-07-09 16:25:35

+3

为什么要使用反射?它比编写一个装载器类相对较慢且更复杂,该装载器类具有如何将一系列字符映射到给定属性的知识。 – 2012-07-09 16:26:41

+0

@ hvd只要我能用单元测试捕捉编译器驱动的中断,对于任何我能想象的解决方案来说,这都应该是微不足道的,我会接受简单性,这对于未来的编译器更改可能很脆弱。 – 2012-07-09 18:50:39

回答

3

使用PropertyInfo.MetadataToken可以看到元数据中出现的属性的顺序。恰巧当前编译器会使该顺序与源代码中属性的显示顺序相匹配,因此按MetadataToken排序,您将获得与源代码中相同的顺序。

声明:未来的编译器可能会改变这一点。如果没有理由,它可能不会,但如果编译器例如变成多线程的话,可能需要额外的不必要的努力来保持原始的顺序。如果您依赖于此,请确保在出现故障时更新.NET Framework时出现严重错误,而不是静默运行时损坏。

1

如果您想使用基于属性的方法,我个人会为此创建自定义属性。这不是一个“标准”操作,所以在框架中没有(适当的)属性可以用来装饰你的类。

我的方法很可能是一个类级属性,它接受列表中每个条目的属性名称的字符串数组,或者沿着这些行的东西。

这就是说,我质疑基于属性的方法是否是正确的方法。您可能需要某种类型的管理员类来介导此操作,因为某些操作需要执行“反射”过程。让这个类管理这里的关系可能更有意义,特别是因为它已经需要关于类层次结构的知识(为了首先构建类)。

在这一点上,拥有一个可以直接构造对象的自定义类或方法会更好地执行,更易于维护,并且比尝试使用反射并动态执行此操作要简单得多。

+0

这个概念是我想通过类似DelimitedData.Parse (str),并且DelimitedData类查看序列的属性,拆分字符串,并将字段分配到模型中的属性以正确的顺序。 +1用于通过明确的列表保存订单。尽管如此,你的解决方案会加倍工作,并且会失去编译时检查该列表中的拼写错误。 – 2012-07-10 18:49:17

1

您使用的是.net 4.0吗?这看起来就像dynamic keyword的创建情况。也就是说,顺序和一致性似乎比任何时候发生的特定类型都要重要,所以你可以随便任意指定标题,数据,无论哪种规则让你快乐,然后将它们拉回来使用相同的规则。这也可能(大概)允许你不使用反射,这总是一个加号。

+0

我想稍后使用静态属性名称进行值绑定的好处 - 例如,我将此模型传递给日志记录事件和视图。我也不确定使用动态过度反射的性能好处 - 我期望CLR处理这两种情况的成本相似。 – 2012-07-10 18:44:36

+0

我不能说我曾经直接使用动态来比较使用反射,但处理动态调用的DLR在逻辑上覆盖了CLR,所以动态关键字的使用决不应该触及CLR。这实际上主要是好奇心,如果你传递了一堆东西,我可能也会倾向于静态属性名称。 – tmesser 2012-07-10 18:48:55

0

你可以看看实现类似于Google's Protocol Buffers的东西。

目前没有C#实现(我知道),但提供的文档非常好,应该给你一些比反射慢得多并且通常比较复杂的想法。

1

现在,如果性能不是一个大问题,并且您正在使用Reflection,那么获取没有属性的映射的简单方法是使用RegEx使用组进行解析。与此相似的实现: Read fixed width record from text file

使用正则表达式,如:

"^(?<Field1>.{6})(?<Field2>.{16})(?<Field3>.{12})" 

既然你可以自己定义组的名称,你可以明智地选择了名称,以完全符合您的属性名称,和这样使用反射自动映射,而不使用属性。

编辑: 既然你会最终有一个字符串,它会不会很“重构友好”里面的属性名称我强烈推荐单元测试这种彻底保证重新命名的属性将打破测试何时产生不匹配。

+0

另一个好建议。可以通过在构建上述Regex时使用Reflection来解决编译安全问题,并且一旦编译了Regex,转换应该相当快。我的意思是,你可以用几个小表达式来构建它,然后像这样得到他们的名字:http://stackoverflow.com/questions/3778598/get-string-property-name-from-expression但是,这可能足够复杂保证简单地使用FileHelper解决方案也在这里提出。 – 2012-07-11 02:55:07

0

当然也有很多可能的答案在这里,所以这里有一个马马虎虎一个我碰到:

有一个在System.ComponentModel.DataAnnotations现有属性(在.net 4.5 +,它移动到系统.ComponentModel.DataAnnotations.Schema)命名ColumnAttribute:

http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.schema.columnattribute(v=vs.110

你可以用它喜欢:

[Column(Order=1)] 
public string Version { get; set; } 

[Column(Order=2)] 
public string Id { get; set; } 

但是,如果固定宽度格式发生变化,这显然很麻烦 - 你必须手动进入并更改你输入的30多个序号,如果说,字段是在开头添加的。由于在这种情况下,我们不控制格式,并且未来的版本可能会频繁出现,所以从类中输入顺序属性的顺序来找到具有隐含顺序的东西将很好。