【问题标题】:How to pass a list of unknown objects of type custom-class containing some properties to method?如何将包含某些属性的自定义类类型的未知对象列表传递给方法?
【发布时间】:2011-03-02 22:48:50
【问题描述】:

我正在创建一个带有访问 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) 我如何实例化未知类型类的对象(在编译时)以便将其添加到列表中而不会导致编译错误?

非常感谢!

【问题讨论】:

  • 不应该在 While(rs.Read()) 循环内移动 _dataItem 的创建吗?否则每次都会插入相同的 _dataItem。也许我错过了什么?问候莫滕

标签: c# list methods instantiation propertyinfo


【解决方案1】:

1:未知类型的列表可能是非泛型IListArrayList、或List&lt;object&gt;

2:Activator.CreateInstance(type)

或者,看看编写一个通用方法,理想情况下是这样的:

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

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

【讨论】:

  • 太棒了!它编译没有问题(多么浪费我以前的时间......)。我明天测试一下。 (事实上​​,我之前发现了 '' - 东西,但并不真正了解如何使用它。对我来说这是一个新概念,曾经与 turbo-pascal 5.5 和 VBA 一起使用......)
【解决方案2】:

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

    #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

【讨论】:

    猜你喜欢
    • 2014-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 2021-02-10
    • 2018-08-16
    • 1970-01-01
    • 2016-06-23
    相关资源
    最近更新 更多