我正在尝试为包含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));
}
}
}
当我编译上面的内容时,我得到“'Col1'是'type参数',在result.Add()行的给定上下文中无效”,因为我没有指定类型必须支持该方法。但是如何?
答案 0 :(得分: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);
}
}
}
答案 1 :(得分:3)
因为接口不能有静态方法,所以你不能(直接)做你所要求的。反射是解决问题的一种方法,但它只在运行时验证,而不是由编译器强制执行。 E.g。
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
方法的现有类型,例如int
和DateTime
。 更新添加了代码,以便它也适用于string
。
答案 2 :(得分:0)
也许这可能会有所帮助。声明泛型函数时,可以指定泛型必须实现interafce。通过此接口,您可以设置Parse()支持。例如:
public void SomeFunction<T>(T variable) where T : IDisposable
{
variable.Dispose();
}
答案 3 :(得分: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);
}
}
}