2

我正在尝试为包含 SQL 查询结果的 XML 字符串创建一个解析器框架。目的是从通用类继承,这些类是用列数据类型实例化的。包含的代码适用于单列品种。两列将有额外的类,等等。

我需要能够指定泛型类型必须支持 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' is a 'type parameter', which is not valid in the given context”,因为我没有指定类型必须支持该方法. 但是怎么做?

4

5 回答 5

7

一种方法是为您的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

因为接口不能有静态方法,所以你不能(直接)做你所要求的。反射是解决问题的一种方法,但它只在运行时验证,而不是由编译器强制执行。例如

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

于 2012-09-09T12:30:34.237 回答
0

也许这会有所帮助。当您声明泛型函数时,您可以指定泛型必须实现接口。通过此接口,您可以设置 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 回答
0

在 .NET 6 中,您可以使用IParseable<T>提供静态抽象方法的接口(也是 .NET 6 中的新方法)Parse

以下是它可能会为您寻找的方式:

abstract class OneColumnParser<Col1> where Col1 : IParseable<Col1>
{
    public abstract string Column1 { get; }

    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, CultureInfo.InvariantCulture));
        }

        return results;
    }
}

这是使其运行的方法,因为截至目前 .NET 6 只有一个候选版本,并且功能处于预览阶段 - https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic -数学/

于 2021-09-21T18:55:12.883 回答