2010-02-24 141 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