试图使用反射来创建通用类型阅读器 [英] trying to create generic Type Reader using reflection

查看:121
本文介绍了试图使用反射来创建通用类型阅读器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题的扩展将静态反射信息传递给静态泛型方法



我试图创建一个泛型类型阅读器,因为我使用类进行大量数据访问,并且我试图创建一个相当通用的方法,允许阅读数据不需要太多的代码。

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



<$ (字段字段,IDataRecord数据)
{
类型type1 = typeof(T); pre $ public static T Read< 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(例外)
{
throw;
}
抛出新的异常(String.Format(Data Type Not Supported:{0},type1));
}

然而,这是抛出一个无效转换异常。



readXXX方法正常工作正常,每个返回语句出现问题



我也试过使用

  public static T SafeConvert< T>(string s,T defaultValue)
{
if(string.IsNullOrEmpty(s))
返回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);

更多...



问我使用什么,最终它允许我在一行中创建一个从任何源读取的可枚举,无论它是CSV还是SQL,通过传递一个文件位置或sql查询即$ /

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

我现在正在运行它,但它需要重写长列表if dataType == typeof(string )在每个实现中,我希望将其重构为单一的通用Read方法,但在Convert中遇到问题。 $ b

  • 您在没有 Binder 的情况下调用 GetMethod ,您如何获得预期的泛型?

  • 读取< T> 会被调用,而没有参数可以用来推断类型,你期待每次当你用类型参数调用它时? 如果2是您的预期,那么和 GetXXX 方法有什么不同?


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

      public static partial class DataReaderExtensions {
    ///< summary>
    ///< para>将资料复制到目标物件< / para>
    ///< para>实现IDataRecord的类通常也实现了IDataReader< / para>
    ///< / summary>
    ///< typeparam name =T>< / typeparam>
    ///< param name =data>< / param>
    ///< param name =target>< / param>
    ///<返回>复制的字段或属性的计数< /返回>
    public static int CopyTo< T>(这个IDataRecord数据,T target){
    return(
    from $ b $中的列)Enumerable.Range(0,data.FieldCount).Select
    (x,i)=> new {
    DataType = data.GetFieldType(i),
    ColumnName = data.GetName(i)
    }

    let typeMeasure()
    let typeMember =
    成员是PropertyInfo
    ?(成员为PropertyInfo).PropertyType
    :member是FieldInfo
    ?(成员为FieldInfo).FieldType
    :default(MemberInfo)
    where typeMember == column.DataType
    let name = member.Name
    where name == column.ColumnName
    let invokeAttr =
    Bindin gFlags.SetProperty | BindingFlags.SetField |
    BindingFlags.NonPublic | BindingFlags.Public |
    BindingFlags.Instance
    select type.InvokeMember(name,invokeAttr,default(Binder),target,new [] {data [name]})
    ).Count();


    $ / code>

    您可以复制数据直接通过类似声明的自定义类型的实例:

      reader.CopyTo(myObject) ; 

    无论公开/非公开,它都会自动将字段名称与字段/属性进行映射。并最终返回复制元素的数量。

    An Extension to this question passing static reflection information to static generic methods

    Im trying to create a Generic Type Reader as i do a lot of data access using Classes and i'm trying to create a fairly generic method that allows to read data without much code.

    the part of the code that does most of the reading looks like this

     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));
        }
    

    However this is throwing an Invalid Cast Exception.

    the readXXX methods are working fine the issue is occuring in each of the return statements

    i have also tried using

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

    but it is still failing

    Edit:

    the method is being called via

    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 );
    

    More...

    People seem to be asking what i use this for, and ultimately it allows me to in one line create an ienumerable that is read from any source be it CSV or SQL by passing a file Location or sql query i.e.

    //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",","); 
    

    I have it running now but it requires repetiiton of the long list of if dataType == typeof(string) in each implementation i was hoping to refactor that into the single generic Read method but am having trouble with the Convert

    解决方案

    1. You invoked GetMethod without a Binder, how can you get the expected one of generics?

    2. Read<T> would be invoked without an argument can used to infer the type, are you expecting that everytime when you invoke it with a type parameter?

    3. If 2 was what you expected, then what's different from just calling GetXXX method?

    I'm unable to follow your original design, but consider the following code:

    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();
        }
    }
    

    You would copy the data directly to a instance of a custom type by statement such like:

    reader.CopyTo(myObject); 
    

    It maps column name with field/property automatically, regardless public/non-public; and finally returns the count of copied elements.

    这篇关于试图使用反射来创建通用类型阅读器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆