2010-02-24 39 views
1

我正在实现用于向数据填充值的“类型无关”方法。 预期的功能是相当简单 - 调用者传递一个需要填充的列值和DataRow的字符串表示的集合:在C中动态字符串解析#

private void FillDataRow(List<ColumnTypeStringRep> rowInput, DataRow row) 

ColumnTypeStringRep结构包含的值,列名的字符串表示,和 - 什么是最重要 - 列数据类型:

private struct ColumnTypeStringRep 
{ 
    public string columnName; public Type type; public string stringRep; 
    public ColumnTypeStrinRep(string columnName, Type type, string stringRep) 
    { 
     this.columnName = columnName; this.type = type; this.stringRep = stringRep; 
    } 
} 

那么,“类型独立性”是什么?基本上我不关心数据行模式(它总是一些类型化的数据表的一行),只要传递的列名匹配DataRow的列名和列数据类型与DataRow匹配就行了。这个功能需要尽可能的灵活(并且尽可能简单 - 只是不简单)。 这是(几乎):

private void FillDataRow(List<ColumnTypeStrinRep> rowInput, DataRow row) 
{ 
Debug.Assert(rowInput.Count == row.Table.Columns.Count); 

foreach (ColumnTypeStrinRep columnInput in rowInput) 
{ 
    Debug.Assert(row.Table.Columns.Contains(columnInput.columnName)); 
    Debug.Assert(row.Table.Columns[columnInput.columnName].DataType == columnInput.type); 

    // --> Now I want something more or less like below (of course the syntax is wrong) : 

    /* 
    row[columnInput.columnName] = columnInput.type.Parse(columnInput.stringRep); 
    */ 

    // --> definitely not like below (this of course would work) : 

    /* 
    switch (columnInput.type.ToString()) 
    { 
     case "System.String": 
      row[columnInput.columnName] = columnInput.stringRep; 
      break; 
     case "System.Single": 
      row[columnInput.columnName] = System.Single.Parse(columnInput.stringRep); 
      break; 
     case "System.DateTime": 
      row[columnInput.columnName] = System.DateTime.Parse(columnInput.stringRep); 
      break; 
     //... 
     default: 
      break; 
    } 
    */ 
} 

}

现在,你可能会看到我的问题 - 我不想使用switch语句。与第一个注释片段一样,这将是完美的,以某种方式使用提供的列类型来调用特定类型的Parse方法,该方法返回由字符串表示形式构造的该类型的对象。 交换机解决方案的工作原理,但它非常不灵活 - 如果将来我将填充不是DataRow,而是一些其他具有可定制类型的“列”的自定义类型(当然,每种类型都需要公开Parse方法从字符串表示构建自己)。

希望你明白我的意思 - 它像“动态解析”类功能。有没有办法在.NET中实现这一点? FillDataRow通话

例子看起来是这样的:

List<ColumnTypeStrinRep> rowInput = new List<ColumnTypeStrinRep>(); 
rowInput.Add(new ColumnTypeStringRep("colName1", Type.GetType("System.Int32"), "0")); 
rowInput.Add(new ColumnTypeStringRep("colName2", Type.GetType("System.Double"), "1,11")); 
rowInput.Add(new ColumnTypeStringRep("colName3", Type.GetType("System.Decimal"), "2,22")); 
rowInput.Add(new ColumnTypeStringRep("colName4", Type.GetType("System.String"), "test")); 
rowInput.Add(new ColumnTypeStringRep("colName5", Type.GetType("System.DateTime"), "2010-01-01")); 
rowInput.Add(new ColumnTypeStringRep("colName6", Type.GetType("System.Single"), "3,33")); 

TestDataSet.TestTableRow newRow = this.testDataSet.TestTable.NewTestTableRow(); 
FillDataRow(rowInput, newRow); 
this.testDataSet.TestTable.AddTestTableRow(newRow); 
this.testDataSet.TestTable.AcceptChanges(); 

谢谢!

回答

2

TypeConverter类是用于转换类型的通用.NET方式。 System.ComponentModel命名空间包含基本类型的实现和WPF附带的更多(但我不知道在哪个命名空间中)。此外,还有静态的Convert类也提供原始类型转换。它自己处理一些简单的转换,并回落到IConvertible

+0

谢谢 - 我会尽快查看。看起来很有希望。我也在考虑“纯粹”的反思 - 对特定类型使用GetMethod(“Parse”),然后通过MethodInfo调用Parse - 我不确定它是否是正确的方式,我无法使其工作。 – garret 2010-02-24 20:28:40

+0

很棒的回答。忘记.NET TypeConverters。 – jrista 2010-02-24 20:29:59

+0

Convert类的附录。您应该能够使用Convert.ChangeType()方法来转换所有内容,而不仅仅是基元。只要你有一个TypeConverter,ChangeType方法应该找到你的类型转换器并且执行转换,只要源和目标类型在某个可用的程序集中某个转换器支持。 – jrista 2010-02-24 20:36:15

0

首先,你使用一个结构来传递一个复杂的数据。这是一个非常糟糕的主意。改为创建一个类。

这就是说,它听起来像你需要一个工厂来创建一个解析器接口的实例:

interface IColumnTypeParser 
{ 
    // A stateles Parse method that takes input and returns output 
    DataColumn Parse(string input); 
} 

class ColumnTyeParserFactory 
{ 
    IColumnTypeParser GetParser(Type columnType) 
    { 
     // Implementation can be anything you want...I would recommend supporting 
     // a configuration file that maps types to parsers, and use pooling or caching 
     // so you are not constantly recreating instances of your parsers (make sure your 
     // parser implementations are stateless to support caching/pooling and reuse) 

     // Dummy implementation: 
     if (columnType == typeof(string)) return new StringColumnTypeParser(); 
     if (columnType == typeof(float)) return new FloatColumnTypeParser(); 
     // ... 
    } 
} 

你FillDataRow实施将HTEN使用工厂:

m_columnTypeParserFactory = new ColumnTypeParserFactory(); 

private void FillDataRow(List<ColumnTypeStrinRep> rowInput, DataRow row) 
{ 
    foreach (ColumnTypeStrinRep columnInput in rowInput) 
    { 
     var parser = m_columnTypeParserFactory.GetParser(columnInput.type); 
     row[columnInput.columnName] parser.Parse(columnInput.stringRep); 
    } 
} 
+0

我同意的结构,但也没有必要重塑类型转换。 – 2010-02-24 20:13:47

+0

感谢您的结构提示!你的解决方案是很好的设计模式重新清新,但这不是我正在寻找的 - 它只是将switch语句从一个地方移动到另一个地方(到ColumnTypeParserFactory)。我正在考虑更多的“.NET automagicall”来玩类型(反射?)。不管怎么说,还是要谢谢你! – garret 2010-02-24 20:22:48

+0

@丹尼尔:啊,你的权利!我有一段时间没有使用.NET TypeConversion,但这是一个更好的解决方案。这使得我的答案大部分是工厂模式中的学术研究。 ;) – jrista 2010-02-24 20:26:46

0

总之有什么像那样 - 没有自动转换。你必须指定转换你自己。这就是说

static class Converters{ 
    static Dictionary<Type, Func<string, object>> Converters = new ... 

    static Converters{ 
     Converters.Add(typeof(string), input => input); 
     Converters.Add(typeof(int), input => int.Parse(input)); 
     Converters.Add(typeof(double), double => double.Parse(input)); 
    } 
} 

void FillDataRow(IList<string> rowInput, row){ 
    for(int i = 0; i < rowInput.Length; i++){ 
     var converter = Converters.Converters[Column[i].DataType]; 
     row[i] = converter(rowInput[i]) 
    } 
} 
+0

这是不对的 - 框架为可扩展类型转换提供了几种选择。 – 2010-02-24 20:12:07

+0

这与jrista解决方案类似(或更准确的解决方案类似于你通过回答时间来判断) - 但是我正在寻找解决方案,我不需要提供逐类型的解决方案 - 无论是jrista's如果你或你的lambda-ing。然而,你的解决方案可能比使用反射类型的魔法更快,我正在寻找。不管怎么说,还是要谢谢你! – garret 2010-02-24 20:37:58

0

Convert.ChangeType怎么样?
你可能会考虑一些泛型btw的幻想。

foreach (ColumnTypeStrinRep columnInput in rowInput) 
{ 
    Debug.Assert(row.Table.Columns.Contains(columnInput.columnName)); 
    Debug.Assert(row.Table.Columns[columnInput.columnName].DataType == columnInput.type); 
    ... 
    row[columnInput.columnName] = Convert.ChangeType(columnInput.stringRep, columnInput.type); 
} 

更多Convert.ChangeType:
http://msdn.microsoft.com/en-us/library/dtb69x08.aspx