我正在解析一个CSV文件并将数据放在一个struct中。我使用的是来自TextFieldParser的这个问题,除了返回一个String[]之外,它的工作原理就像一种魅力。目前,我有一个丑陋的过程:
String[] row = parser.ReadFields();
DispatchCall call = new DispatchCall();
if (!int.TryParse(row[0], out call.AccountID)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
continue;
}
call.WorkOrder = row[1];
call.Description = row[2];
call.Date = row[3];
call.RequestedDate = row[4];
call.EstStartDate = row[5];
call.CustomerID = row[6];
call.CustomerName = row[7];
call.Caller = row[8];
call.EquipmentID = row[9];
call.Item = row[10];
call.TerritoryDesc = row[11];
call.Technician = row[12];
call.BillCode = row[13];
call.CallType = row[14];
call.Priority = row[15];
call.Status = row[16];
call.Comment = row[17];
call.Street = row[18];
call.City = row[19];
call.State = row[20];
call.Zip = row[21];
call.EquipRemarks = row[22];
call.Contact = row[23];
call.ContactPhone = row[24];
call.Lat = row[25];
call.Lon = row[26];
call.FlagColor = row[27];
call.TextColor = row[28];
call.MarkerName = row[29];该结构由所有属于String的字段组成,除了AccountID是一个int。它们不是强类型的,这让我很恼火,但是现在让我们看一看。如果parser.ReadFields()返回一个String[],那么是否有一种更有效的方法来用数组中的值填充一个结构(可能会转换一些值,比如需要成为int的row[0] )?
**编辑:**我忘了提到的一个限制可能会影响什么样的解决方案将起作用,那就是这个结构是[Serializable],并且会被发送到其他地方。
发布于 2012-08-17 16:00:04
您的里程可能取决于它是否是更好的解决方案,但您可以使用反射并定义用于标记结构成员的Attribute类。该属性将以数组索引作为参数。然后通过使用反射来分配来自正确数组元素的值。
您可以像这样定义属性:
[AttributeUsage(AttributeTargets.Property)]
public sealed class ArrayStructFieldAttribute : Attribute
{
public ArrayStructFieldAttribute(int index)
{
this.index = index;
}
private readonly int index;
public int Index {
get {
return index;
}
}
}这意味着可以简单地使用属性将名为int的Index值与属性关联起来。
然后,您可以使用该属性在结构中标记属性(只是一些示例行):
[ArrayStructField(1)]
public string WorkOrder { // ...
[ArrayStructField(19)]
public string City { // ...然后可以使用结构类型的Type对象设置值(您可以使用typeof操作符获得它):
foreach (PropertyInfo prop in structType.GetProperties()) {
ArrayStructFieldAttribute attr = prop.GetCustomAttributes(typeof(ArrayStructFieldAttribute), false).Cast<ArrayStructFieldAttribute>().FirstOrDefault();
if (attr != null) {
// we have found a property that you want to load from an array element!
if (prop.PropertyType == typeof(string)) {
// the property is a string property, no conversion required
prop.SetValue(boxedStruct, row[attr.Index]);
} else if (prop.PropertyType == typeof(int)) {
// the property is an int property, conversion required
int value;
if (!int.TryParse(row[attr.Index], out value)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
} else {
prop.SetValue(boxedStruct, value);
}
}
}
}此代码迭代结构类型的所有属性。对于每个属性,它检查上面定义的自定义属性类型。如果存在这样的属性,并且如果属性类型为string或int,则从相应的数组索引复制值。
我正在检查string和int属性,因为这是您在问题中提到的两种数据类型。即使您现在只有一个包含int值的特定索引,但如果此代码准备将任何索引作为字符串或int属性处理,则对可维护性很好。
请注意,对于更多要处理的类型,我建议不要使用if和else if链,而是使用将属性类型映射到转换函数的Dictionary<Type, Func<string, object>>。
发布于 2012-08-17 16:11:31
如果您想要创建非常灵活的东西,可以使用自定义属性在DispatchCall上标记每个属性。就像这样:
class DispatchCall {
[CsvColumn(0)]
public Int32 AccountId { get; set; }
[CsvColumn(1)]
public String WorkOrder { get; set; }
[CsvColumn(3, Format = "yyyy-MM-dd")]
public DateTime Date { get; set; }
}这允许您将每个属性与列关联起来。对于每一行,您可以对所有属性进行迭代,通过使用属性,您可以将正确的值分配给正确的属性。您将不得不从字符串到数字、日期和可能的枚举进行某种类型转换。您可以向属性添加额外的属性,以帮助您完成该过程。在示例中,我发明了Format,在解析DateTime时应该使用它:
Object ParseValue(String value, TargetType targetType, String format) {
if (targetType == typeof(String))
return value;
if (targetType == typeof(Int32))
return Int32.Parse(value);
if (targetType == typeof(DateTime))
DateTime.ParseExact(value, format, CultureInfo.InvariantCulture);
...
}在上面的代码中使用TryParse方法可以通过允许您在遇到不可解析的值时提供更多的上下文来改进错误处理。
不幸的是,这种方法并不十分有效,因为将对输入文件中的每一行执行反射代码。如果您想提高效率,您需要动态地创建一个编译的方法,方法是在DispatchCall上进行一次反射,然后可以在每一行上应用。这是可能的,但并不是特别容易。
发布于 2012-08-17 16:25:49
你对你正在使用的库有多依赖?我发现档案助手对于这类事情非常有用。您的代码应该类似于:
using FileHelpers;
// ...
[DelimitedRecord(",")]
class DispatchCall {
// Just make sure these are in order
public int AccountID { get; set; }
public string WorkOrder { get; set; }
public string Description { get; set; }
// ...
}
// And then to call the code
var engine = new FileHelperEngine(typeof(DispatchCall));
engine.Options.IgnoreFirstLines = 1; // If you have a header row
DispatchCall[] data = engine.ReadFile(FileName) as DispatchCall[];现在有了一个DispatchCall数组,引擎为您完成了所有的繁重工作。
https://stackoverflow.com/questions/12009104
复制相似问题