2

SQL クエリの結果を含む XML 文字列用のパーサー フレームワークを作成しようとしています。その意図は、列のデータ型でインスタンス化されたジェネリック クラスから継承することです。含まれているコードは、単一列の種類のものです。2 つの列などの追加のクラスがあります。

ジェネリック型が 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));
        }
    }
}

上記をコンパイルすると、型がメソッドをサポートする必要があることを指定していないため、results.Add() 行に「'Col1' は '型パラメーター' です。指定されたコンテキストでは有効ではありません」と表示されます。 . しかし、どのように?

4

5 に答える 5

7

1 つの方法は、パラメーターなしのコンストラクターと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);
        }
    }
}
于 2012-09-09T12:23:48.520 に答える
3

インターフェイスは静的メソッドを持つことができないため、要求していることを(直接)実行することはできません。リフレクションは問題を解決する1つの方法ですが、実行時にのみ検証され、コンパイラーによって強制されることはありません。例えば

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ます。 追加されたコードを更新して、同様に機能するようにします。intDateTimestring

于 2012-09-09T12:30:34.237 に答える
0

多分これは役立つかもしれません。ジェネリック関数を宣言するとき、ジェネリックがinterafceを実装する必要があることを指定できます。このインターフェースにより、Parse()サポートを設定できます。例えば:

public void SomeFunction<T>(T variable) where T : IDisposable
       {
           variable.Dispose();
       }
于 2012-09-09T12:28:02.430 に答える
0

あなたの質問に対する直接的な答えではありませんが、カスタム構成セクションハンドラーから型指定された値を返すために使用する汎用文字列アンボクサークラスのコードを次に示します。いくつかのアイデアが得られるかもしれません...

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);
        }
    }
}
于 2012-09-09T15:15:27.423 に答える