2012-09-09 101 views
1

我正在嘗試爲包含SQL查詢結果的XML字符串創建解析器框架。目的是從泛型類繼承,泛型類使用列數據類型實例化。所包含的代碼適用於單列變體。將有兩列的額外的類,等等。如何指定C#泛型類型必須支持Parse(string)?

我需要能夠指定通用類型必須支持解析(字符串)方法。我該怎麼做呢?

abstract class OneColumnParser<Col1> 
{ 
    abstract string Column1; 

    List<Col1> ParseQueryResult(string queryResult) 
    { 
     XmlDocument xDoc = new XmlDocument(); 
     xDoc.LoadXml(queryResult); 
     List<Col1> results = new List<Col1>(); 

     foreach (XmlNode xNode in xDoc.GetElementsByTagName(Column1)) 
     { 
      results.Add(Col1.Parse(xNode.InnerText)); 
     } 
    } 
} 

當我編譯上面,我得到「‘Col1中’是‘類型參數’,這是不在給定上下文中有效」關於results.Add()線,因爲我還沒有指定的該類型必須支持該方法。但是如何?

+2

'Parse()'是一個靜態方法,所以直接回答:你不行。 –

+0

您打算在此支持哪些類型的產品?什麼類型是「結果」? –

+0

簡單的;主要是字符串,整數,布爾值。每列都是單一類型的,但我希望能夠支持多列查詢,其中列是不同類型的 - 但每個列支持Parse()。 – Uffe

回答

3

因爲接口不能有靜態方法,所以你不能(直接)做你所要求的。反射是解決問題的一種方法,但它只能在運行時驗證,而不是由編譯器強制執行。例如。

abstract class OneColumnParser<TCol> 
{ 
    private static MethodInfo ParseInfo = typeof(TCol).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null); 
    abstract string Column1; 

    static OneColumnParser() 
    { 
     if (typeof(TCol) != typeof(string) && (ParseInfo == null || !typeof(TCol).IsAssignableFrom(ParseInfo.ReturnType))) 
      throw new InvalidOperationException("Invalid type, must contain public static TCol Parse(string)"); 
    } 

    private static TCol Parse(string value) 
    { 
     if (typeof(TCol) == typeof(string)) 
      return (TCol)(object)value; 
     else 
      return (TCol)ParseInfo.Invoke(null, new[] { value }); 
    } 

    public List<TCol> ParseQueryResult(string queryResult) 
    { 
     XmlDocument xDoc = new XmlDocument(); 
     xDoc.LoadXml(queryResult); 
     List<TCol> results = new List<TCol>(); 

     foreach (XmlNode xNode in xDoc.GetElementsByTagName(Column1)) 
     { 
      results.Add(Parse(xNode.InnerText)); 
     } 

     return results; 
    } 
} 

與定義自己的接口,這將在現有的工作類型與Parse方法,如intDateTime更新添加的代碼,以便它也可以在string上工作。

+0

這是一個嚴重的漂亮的編碼在那裏。謝謝! – Uffe

+0

謝謝! Downvoter照顧解釋? –

6

的一種方法是定義一個參數的構造函數和接口爲您Col1類型:

interface IParseable 
{ 
    void Parse(string text); 
} 

abstract class OneColumnParser<T> where T : IParseable, new 
{ 
    abstract string Column1; 

    List<T> ParseQueryResult<T>(string queryResult) 
    { 
     XmlDocument xDoc = new XmlDocument(); 
     xDoc.LoadXml(queryResult); 
     var results = new List<T>(); 

     foreach (XmlNode xNode in xDoc.GetElementsByTagName(Column1)) 
     { 
      var col = new T(); 
      col.Parse(xNode.InnerText); 
      results.Add(col); 
     } 
    } 
} 
+0

以及如何將IP地址附加到int和double? –

+0

@HenkHolterman - OP將不得不實施許多版本的'IParseable'。 –

+1

除非他定義了他自己的靈長類動物,否則他將無法使用這種方法.....我猜 – Star

0

也許這將有助於。當你聲明泛型函數時,你可以指定泛型必須實現交互。通過這個接口你可以設置Parse()支持。例如:

public void SomeFunction<T>(T variable) where T : IDisposable 
     { 
      variable.Dispose(); 
     } 
0

不是直接回答你的問題,但這裏是我用來從一個自定義配置節處理程序返回類型的值,可能會給你一些想法的一般串unboxer類的代碼...

using System.ComponentModel; 
using System.Data.SqlTypes; 
using System.Threading; 

public static class StringUnboxer<T> { 
    private static readonly object _lock = new object(); 
    private static T m_convertedValue = default(T); 

    public static T unBox(string value) { 
     try { 
      Monitor.Enter(_lock); 
      // Test to see if value is valid to convert to supplied type 
      if (canUnBox(value)) { 
       // value is valid, return conversion 
       return m_convertedValue; 
      } 
      else { 
       // Conversion not possible with given string data, return default value for supplied type 
       switch (typeof(T).ToString()) { 
        // In our case, if the supplied type is System.DateTime, we want to return 
        // System.Data.SQLTypes.SQLDateTime.MinValue (01/01/1753) instead of 
        // System.DateTime.MinValue (01/01/0001) which is the normal default value 
        case "System.DateTime": 
         return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(SqlDateTime.MinValue.ToString()); 
        // Return the .NET default value for all other types 
        default: 
         return default(T); 
       } 
      } 
     } 
     finally { 
      Monitor.Exit(_lock); 
     } 
    } 

    private static bool canUnBox(string value) { 
     try { 
      Monitor.Enter(_lock); 
      m_convertedValue = (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(value); 
      return true; 
     } 
     catch { 
      return false; 
     } 
     finally { 
      Monitor.Exit(_lock); 
     } 
    } 
} 
相關問題