【问题标题】:trying to create generic Type Reader using reflection尝试使用反射创建通用类型阅读器
【发布时间】:2013-03-27 18:48:40
【问题描述】:

这个问题的扩展passing static reflection information to static generic methods

我正在尝试创建一个通用类型读取器,因为我使用类进行大量数据访问,并且我正在尝试创建一个相当通用的方法,允许在没有太多代码的情况下读取数据。

完成大部分阅读的代码部分看起来像这样

 public static T Read<T>(string field,IDataRecord data )
    {
        Type type1 = typeof (T);
        try
        {
            if (type1 == typeof( String ))
            {
                return (T)Convert.ChangeType( readString( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( int? ))
            {
                return (T)Convert.ChangeType( readIntN( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( Guid? ))
            {
                return (T)Convert.ChangeType( readGuidN( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( double? ))
            {
                return (T)Convert.ChangeType( readDoubleN( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( decimal? ))
            {
                var res = readDecimalN(data[field].ToString());
                return (T)Convert.ChangeType( res, typeof( T ) );
            }
            if (type1 == typeof( float? ))
            {
                return (T)Convert.ChangeType( readFloatN( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( bool? ))
            {
                return (T)Convert.ChangeType( readBoolN( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( DateTime? ))
            {
                return (T)Convert.ChangeType( readDatetimeN( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( int ))
            {
                return (T)Convert.ChangeType( readInt( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( long? ))
            {
                return (T)Convert.ChangeType( readLongN( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( long ))
            {
                return (T)Convert.ChangeType( readLong( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( Guid ))
            {
                return (T)Convert.ChangeType(readGuid( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( double ))
            {
                return (T)Convert.ChangeType( readDouble( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( decimal ))
            {
                return (T)Convert.ChangeType( readDecimal( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( float ) || type1 == typeof( Single ))
            {
                return (T)Convert.ChangeType( readFloat( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( bool ))
            {
                return (T)Convert.ChangeType( readBool( data[field].ToString() ), typeof( T ) );
            }
            if (type1 == typeof( DateTime ))
            {
                return (T)Convert.ChangeType( readDatetime( data[field].ToString() ), typeof( T ) );
            }
        }
        catch (Exception)
        {
            throw;
        }
        throw new Exception(String.Format("Data Type Not Supported: {0}", type1));
    }

但是,这会引发 Invalid Cast Exception。

readXXX 方法运行良好,但每个返回语句都出现问题

我也尝试过使用

public static T SafeConvert<T>(string s, T defaultValue)
{
if ( string.IsNullOrEmpty(s) )
    return defaultValue;
return (T)Convert.ChangeType(s, typeof(T));
} 

但还是失败了

编辑:

方法正在通过

调用
private static List<T> GetItems<T>(IDataReader reader)
    {
        var items = new List<T>();
        while (reader.Read())
        {
            Type type1 = typeof (T);
            var item = (T) Activator.CreateInstance(typeof (T), new object[] {});
            foreach (PropertyInfo info in type1.GetProperties())
            {
                int written = 0;
                if (info.CanWrite)
                {
                    #region

                    try
                    {
                        Type dataType = info.PropertyType;
                        MethodInfo method = typeof (DataReader).GetMethod("Read",BindingFlags.Static | BindingFlags.Public);
                        MethodInfo generic = method.MakeGenericMethod(dataType);
                        var t = generic.Invoke(null, new object[] {info.Name, reader});
                        info.SetValue( item, t );

更多...

人们似乎在问我用它做什么,最终它允许我在一行中创建一个 ienumerable,它可以通过传递文件位置或 sql 查询从任何来源读取,无论是 CSV 还是 SQL,即

//returns Ienumerable<MyClass>
var list = Reader.ReadSql<MyClass>(DataBases.Test,"select * from TestTable where someAttribute  = 1");
// also returns Ienumerable MyClass
var list2 = Readre.ReadCsv<MyClass>(@"c:\file1.csv",","); 

我现在运行它,但它需要在每个实现中重复 if dataType == typeof(string) 的长列表我希望将其重构为单个通用读取方法,但在转换时遇到问题

【问题讨论】:

  • 您已经发布了 很多 代码,但没有显示您是如何调用该方法或值是什么。这很难帮助你。此外,您的 try/catch 块毫无意义 - 您只是重新抛出原始异常。
  • 您没有说,但我猜对于不实现 IConvertable 的类型(例如 Guid)会引发异常。您必须为这些类型调用构造函数。
  • 嗨,乔恩,调用是在上一个问题中进行的,如顶部链接,我将在这个问题中添加更多细节(是的,我知道 try catch 是不必要的,目前使用它进行调试)
  • 嗯...看起来您可能正在尝试制作 DataTable 的“类型安全”版本...准确吗?
  • @Yakyb,如果我没看错,您是在尝试手动实现序列化吗?为什么不使用内置的 C# 序列化方法/接口?

标签: c# generics reflection


【解决方案1】:

您更新的问题说它有效,但您想重构。好吧,你可以!

Dictionary<Type, Func<string, IDataRecord, object>> converters_ = new Dictionary<Type, Func<string, IDataRecord, object>>();

converters_[typeof(bool)] = (s) => { return readBoolN( data[s].ToString() ); };
// repeat for other types:

Then

 public static T Read<T>(string field,IDataRecord data )
 {
     return (T)converters_[typeof(T)](field, data);
 }

【讨论】:

    【解决方案2】:
    1. 您在没有Binder 的情况下调用了GetMethod,您如何获得预期的泛型之一?

    2. Read&lt;T&gt; 将在没有参数的情况下被调用来推断类型,您是否期望每次使用类型参数调用它?

    3. 如果 2 是您所期望的,那么与仅调用 GetXXX 方法有什么不同?

    我无法遵循您的原始设计,但请考虑以下代码:

    public static partial class DataReaderExtensions {
        /// <summary>
        /// <para>Copy data to target object</para>
        /// <para>Class which implements IDataRecord usually also implements IDataReader</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="target"></param>
        /// <returns>the count of field or property copied</returns>
        public static int CopyTo<T>(this IDataRecord data, T target) {
            return (
                from column in
                    Enumerable.Range(0, data.FieldCount).Select(
                        (x, i) => new {
                            DataType=data.GetFieldType(i),
                            ColumnName=data.GetName(i)
                        }
                        )
                let type=target.GetType()
                from member in type.GetMembers()
                let typeMember=
                    member is PropertyInfo
                        ?(member as PropertyInfo).PropertyType
                        :member is FieldInfo
                            ?(member as FieldInfo).FieldType
                            :default(MemberInfo)
                where typeMember==column.DataType
                let name=member.Name
                where name==column.ColumnName
                let invokeAttr=
                    BindingFlags.SetProperty|BindingFlags.SetField|
                    BindingFlags.NonPublic|BindingFlags.Public|
                    BindingFlags.Instance
                select type.InvokeMember(name, invokeAttr, default(Binder), target, new[] { data[name] })
                ).Count();
        }
    }
    

    您可以通过语句将data 直接复制到自定义类型的实例,例如:

    reader.CopyTo(myObject); 
    

    无论公共/非公共,它都会自动将列名与字段/属性映射;最后返回复制元素的计数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 1970-01-01
      • 1970-01-01
      • 2017-03-29
      • 2023-02-12
      相关资源
      最近更新 更多