关于 c# 接口和泛型的问题

Posted

技术标签:

【中文标题】关于 c# 接口和泛型的问题【英文标题】:A question regarding c# interfaces and maybe generics 【发布时间】:2011-04-28 00:01:58 【问题描述】:

我们已经构建了一个内部工具来生成整个数据访问,每个表都有一个代表它的数据和所有常见操作的类。(想想轻量级实体框架)。

这些 DataAccess 对象总是有一个接收连接字符串的构造函数和一个接收 SqlDataReader 的 Load 函数。

类似这样的:

class Customer

    public string ConnStr;
    public int Id;
    public string Name;

    Public customers(string connStr)
    
        ConnStr = connStr;
    

    public Customer Load(SqlDataReader)
    
        if(reader.Read())
        
            Id = reader["Id"].ToString();
            Name = reader["Name"].ToString();
           
    

我想编写一个实用程序数据访问静态方法,该方法允许我编写我的 SQL 并获得一个对象列表作为回报,遵循前面的对象示例:

string SQL = "SELECT * FROM Customers WHERE Name=@Name";
List<Customer> customers = GetList<Customer>(connStr, SQL, new SqlParameters("@Name", "John"));

我不知道如何处理它,我尝试过接口,但它们不允许构造函数或静态方法,并且使用泛型 - 我无法调用初始化对象所需的方法(构造函数 +加载),这是我最近的尝试,注释掉了不起作用的部分:


public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
        
            List<T> list = new List<T>();

            using (SqlConnection conn = new SqlConnection(connStr))
            
                conn.Open();

                SqlCommand cmd = new SqlCommand(SQL, conn);
                foreach (SqlParameter param in prms)
                
                    cmd.Parameters.Add(param);
                

                using (SqlDataReader reader = cmd.ExecuteReader())
                

                    //T item = new T(connStr);
                    //item.Load(reader);
                    //list.Add(item);
                
            

            return list;
        

顺便说一句,我们有兴趣开源我们的 DataAccess 生成器,这太棒了 - 它允许非常有效地访问数据库对象 + 创建一个 javascript 数据访问层,让您可以从 javascript 完全控制您的数据库(当然这有安全隐患可以管理)。

如果这里有人知道如何“开源”这样的项目或任何想加入该产品开发的公司 - 请随时与我联系:eytan@titkadem.co.il

提前致谢, 伊坦

【问题讨论】:

OT,但从纯粹主义者的角度来看,那些域对象对持久性了解很多,这不一定是他们的责任。我还认为 DataContext 可以为您完成大部分绑定,而无需实体中的任何数据访问代码,免费 ExecuteQuery&lt;T&gt; Lasse 提出了一个有趣的观点,即谁调用了 Read;在许多方面,最好接受IDataRecord 而不是IDataReader/SqlDataReader - 假设它们只需要一行。 【参考方案1】:

Load 很简单 - 你可以:

interface IDataEntity 
    void Load(SqlDataReader reader);

然后:

public static List<T> GetList<T>(string connStr, string SQL,
      params SqlParameter[] prms) where T : IDataEntity, new()

    .... 
    T item = new T();
    item.Load(reader);
    list.Add(item);

new T(connStr) 更棘手 - 它真的需要这个值吗?公共属性会更容易

interface IDataEntity 
    void Load(SqlDataReader reader);
    string ConnectionString get;set;

class Customer : IDataEntity 
 ... 

等等。参数化的泛型构造函数没有任何内置(语言)支持。您可以绕过它,但在许多情况下,Activator.CreateInstance 的参数化形式足够快(与通过网络访问数据相比,反射可以忽略不计)。如果您需要参数化版本,可以使用Expression 等来完成(如果您需要示例,请告诉我)。

【讨论】:

嗨 Marc,非常感谢,唯一的更正是 new() 约束应该出现在 IDataEntity 之后。【参考方案2】:

你需要进行反射,除非你想改变你的类以构造函数的方式工作。

你可以这样做:

while (reader.Read())

    T item = Activator.CreateInstance(typeof(T), new object[]  connStr ) as T;
    item.Load(reader);
    list.Add(item);

(请注意,上述循环的职责是调用阅读器上的 .Read)

请注意,除非您通过接口公开 Load 方法,否则您还需要通过反射调用它。

我会这样做:

public interface IDataObject

    void Load(IDataReader reader);

然后为您的客户实施这个:

public class Customer : IDataObject

    ...

然后在你的 GetList 方法中添加一个约束:

public List<T> GetList<T>(string connStr, string sql, params SqlParameter[] parameters)
    where T : IDataObject

【讨论】:

Read 的好地方;实际上,我想知道IDataRecord 会不会更好。 使用 IDataRecord 可能会更好,同意。【参考方案3】:

这不会如您所愿。 .net 的 SQL 类不是强类型的。不可能从像 SELECT * FROM Customers... 这样的查询中说出必须构造什么类的对象。如果您的所有实体都派生自一个声明的类,这是可能的:

public virtual Entity Load(SqlDataReader reader)

你的泛型方法有约束:

public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
   where T : Entity, new()

那么你可以这样做:

T entity = new T();
entity.Load(reader);

【讨论】:

【参考方案4】:

为什么不将 const 传递给方法:

T item = new T();
item.SetConn(connStr);

别忘了:

public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
  where T : new

【讨论】:

以上是关于关于 c# 接口和泛型的问题的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中,关于可变参数和泛型的问题。

Java中数组和泛型的类型规则

C# 泛型的使用

初识Java集合及包装类和泛型的基本使用

2018.6.11 集合和泛型的课后练习总结

C#方法重载和泛型接口