如何将包含某些属性的custom-class类型的未知对象列表传递给方法?

时间:2011-03-02 22:48:50

标签: c# list methods instantiation propertyinfo

我正在创建一个databasehelper类,其中包含访问SQLCE数据库的方法。我想使用相同的方法使用包含与不同表中的字段匹配的属性的不同类来读取行。要使用的类是在运行时确定的,我想将带有类的对象的列表传递给方法,并获取属性名并使用它们来读取数据库。非常方便,因为我可以将它用于我的所有(SQLCE-)数据库。

(我更新了错误的代码以便在此提供解决方案)

    #region ReadData
    ///----------------------------------------------------------------------
    /// <summary>
    /// Reads datarows from database and adds them to list.
    /// </summary>
    /// <param name="data">List containing objects with properties.</param>
    /// <param name="table">Table in database.</param>
    /// <param name="search">Substring of SQL-statement that follows 'WHERE'.</param>
    /// <param name="connect">Connectionstring.</param>
    /// <returns>true if successfull</returns>
    ///----------------------------------------------------------------------
    public static bool ReadData<T>(List<T> data, string table, string search, string connect) where T : class, new()
    {
        // Return if input id missing
        if (data == null || table == "" || connect == "") return false;

        // retrieve properties from Data 
        PropertyInfo[] propinf = typeof(T).GetProperties();

        // Create string with SQL-statement
        string fields = "";
        // retrieve fields from propinf
        foreach (PropertyInfo p in propinf)
        {
            fields += fields == "" ? p.Name : ", " + p.Name;
        }
        // create SQL SELECT statement with properties and search
        string sql = "SELECT " + fields + " FROM " + table;
        sql += search == "" ? "" : " WHERE " + search;

        // Instantiate and open database
        SqlCeConnection cn = new SqlCeConnection(connect);
        if (cn.State == ConnectionState.Closed)
        cn.Open();
        data.Clear();   // just in case
        try
        {
            SqlCeCommand cmd = new SqlCeCommand(sql, cn);
            cmd.CommandType = CommandType.Text;
            SqlCeResultSet rs = cmd.ExecuteResultSet(ResultSetOptions.Scrollable);
            if (rs.HasRows)  // Only if database is not empty
            {
                while (rs.Read()) // read database
                {
                    // instantiate single item of list Data
                    var dataitem = new T();
                    int ordinal = 0;
                    foreach (PropertyInfo p in propinf)
                    {
                        // read database and
                        PropertyInfo singlepropinf = typeof(T).GetProperty(p.Name);
                        ordinal = rs.GetOrdinal(p.Name);
                        singlepropinf.SetValue(dataitem, rs.GetValue(ordinal), null); // fill data item
                    }
                    data.Add(dataitem);  // and add it to data.
                }
            }
            else
            {
                MessageBox.Show("No records matching '" + search + "'!");
                return false;
            }
        }
        catch (SqlCeException sqlexception)
        {
            MessageBox.Show(sqlexception.Message, "SQL-error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return false;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return false;
        }
        finally
        {
            cn.Close();
        }
        return true;
    }
    #endregion

我有两个问题:

1)如何以未知类型传递此列表?到目前为止我发现的答案并没有帮助我解决这个问题。

2)如何实例化一个未知类型的对象(在编译时),以便将它添加到List而不会导致编译错误?

非常感谢!

2 个答案:

答案 0 :(得分:5)

1:未知类型的列表可以是非通用IList,或ArrayListList<object>

2:Activator.CreateInstance(type)

或者,看一下编写泛型方法,理想情况如下:

ReadData<T>(List<T> data, ...) where T : class, new()

并使用new T()创建新项目,并使用typeof(T)来讨论Type。使用通用方法,调用者通常隐式地提供T。请注意,您的示例中不需要ref

答案 1 :(得分:0)

下面是对代码的更新。它接近最终并已经在各种不同的情况下进行了测试。理想情况下,使用反射的迭代必须被性能较低的东西所取代,但只要数据库操作更耗时,我想它在现实生活中并不重要。我已经很满意了。

    #region Read(Like)Data
    public static int ReadData<T>(List<T> data, string table, T search, string connect) where T : class, new()
    {
        return BaseRead(data, table, search, connect, "=");
    }
    public static int ReadLikeData<T>(List<T> data, string table, T search, string connect) where T : class, new()
    {
        return BaseRead(data, table, search, connect, "LIKE");
    }
    ///----------------------------------------------------------------------
    /// <summary>
    /// Reads datarows from database and adds them to list containing objects of type T.
    /// Note that the properties of T should match the fields of the database table.
    /// </summary>
    /// <param name="data">List containing objects of type T with properties matching fields in table.</param>
    /// <param name="table">Table in database.</param>
    /// <param name="search">Object of type T with (some) properties containing search constraints, 
    /// others should be null. Unused DateTime should be 1800-01-01.</param>
    /// <param name="connect">Connectionstring.</param>
    /// <returns>-1 if exception was thrown or the number of records (objects of type T) otherwise</returns>
    ///----------------------------------------------------------------------
    private static int BaseRead<T>(List<T> data, string table, T search, string connect, string comparer) where T : class, new()
    {
        // Abort if insufficient arguments
        if (data == null || table == "" || connect == "") return 0;
        // Make sure List<T> data is empty
        data.Clear();
        // Retrieve properties from object of type T 
        PropertyInfo[] propinfs = typeof(T).GetProperties();

        // -----------------------------------------
        // Create string that contains SQL-statement
        // -----------------------------------------
        string fields = ""; string wherestr = "";
        // Retrieve fields from propinf
        foreach (PropertyInfo p in propinfs)
        {
            fields += fields == "" ? p.Name : ", " + p.Name;
            dynamic propvalue = p.GetValue(search, null);
            // Solutions for properties of type DateTime
            long dateticks = 0; DateTime dt = new DateTime();
            Type type = propvalue != null ? propvalue.GetType() : null;
            if (propvalue != null && propvalue.GetType() == dt.GetType())
            {
                dt = propvalue;
                dateticks = dt.Ticks;
            }
            // DateTime 1800-01-01 equals null (hey, it's better than nothing...)
            if (propvalue != null && dt != DateTimeNull)
                wherestr += wherestr == "" ? p.Name + " " + comparer + " @" + p.Name.ToLower() 
                    : " AND " + p.Name + " " + comparer + " @" + p.Name.ToLower();
        }
        // Create SQL SELECT statement with properties and search
        string sql = "SELECT " + fields + " FROM " + table;
        sql += wherestr == "" ? "" : " WHERE " + wherestr;

        // -------------------
        // Database operations
        // -------------------
        SqlCeConnection cn = new SqlCeConnection(connect);
        if (cn.State == ConnectionState.Closed) cn.Open();
        try
        {
            SqlCeCommand cmd = new SqlCeCommand(sql, cn);
            cmd.CommandType = CommandType.Text;
            // Add propertyvalues to WHERE-statement using reflection
            foreach (PropertyInfo p in propinfs)
            {
                dynamic propvalue = p.GetValue(search, null);
                // Except for DateTime values 1800-01-01 (defined as null)
                if (propvalue != null && !(propvalue.GetType() is DateTime && propvalue != DateTimeNull))
                {
                    if (comparer == "LIKE") propvalue = "%" + propvalue + "%";
                    cmd.Parameters.AddWithValue("@" + p.Name.ToLower(), propvalue);
                }
            }
            SqlCeResultSet rs = cmd.ExecuteResultSet(ResultSetOptions.Scrollable);
            if (rs.HasRows)  // Only if database is not empty
            {
                while (rs.Read()) // Read next row in database
                {
                    // Instantiate single item of List data
                    var dataitem = new T();  // Object to put the field-values in
                    foreach (PropertyInfo p in propinfs)
                    {
                        // Read database fields using reflection
                        PropertyInfo singlepropinf = typeof(T).GetProperty(p.Name);
                        int ordinal = rs.GetOrdinal(p.Name);
                        dynamic result = rs.GetValue(ordinal);
                        // Conversion to null in case field is DBNull
                        if (result is DBNull)
                        {
                            if (singlepropinf.PropertyType.Equals(typeof(DateTime)))
                            {
                                singlepropinf.SetValue(dataitem, DateTimeNull, null); // Fill data item with datetimenull
                            }
                            else
                            {
                                singlepropinf.SetValue(dataitem, null, null); // Fill data item with null
                            }
                        }
                        else
                        {
                            singlepropinf.SetValue(dataitem, result, null); // Or fill data item with value
                        }
                    }
                    data.Add(dataitem);  // And add the record to List<T> data.
                }
            }
            else
            {
                //MessageBox.Show("No records matching '" + wherestr + "'!");
                return 0;
            }
        }
        catch (SqlCeException sqlexception)
        {
            MessageBox.Show(sqlexception.Message, "SQL-error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return -1;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return -1;
        }
        finally
        {
            cn.Close();
        }
        // Return number of objects (should equal number of retrieved records)
        return data.Count();
    }
    #endregion