通用方法,其中T实现Interface

本文关键字:实现 Interface 其中 方法 | 更新日期: 2023-09-27 17:52:39

我正在尝试创建一个通用的数据检索过程。我目前的工作,但有一部分似乎不太正确,我希望有一个更好的方法来完成它。

所以我的想法是,我为数据库中的每个表都有类,这里是一个类的例子:

public class CMCGRGRGROUP : IFacetsObject<CMCGRGRGROUP>
{
    public int GRGR_CK { get; set; }
    public string GRGR_NAME { get; set; }
    public string GRGR_ADDR1 { get; set; }
    public IEnumerable<CMCGRGRGROUP> ToObject(DataTable table)
    {
        return table.AsEnumerable().Select(row =>
        {
            return new CMCGRGRGROUP
            {
                GRGR_CK = Convert.ToInt32(row["GRGR_CK"]),
                GRGR_NAME = row["GRGR_NAME"].ToString(),
                GRGR_ADDR1 = row["GRGR_ADDR1"].ToString()
            };
        });
    }
}

您将注意到该类实现了自己类型的接口。接口只定义了一个名为ToObject的方法,该方法用于将数据表转换为特定类型的类:

public interface IFacetsObject<T>
{
    IEnumerable<T> ToObject(DataTable obj);
}

现在,这是我用来执行查询的方法:

public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) where T : new()
{
    using (var conn = new AseConnection(_conn))
    {
        conn.Open();
        var cmd = new AseCommand(sql, conn);
        var dt = new DataTable();
        var da = new AseDataAdapter(sql, conn);
        da.Fill(dt);                
        return obj.ToObject(dt); //this is the interface method
    }
}

所以主要的问题是:泛型方法如何知道T应该实现IFacetsObject<T> ?这样我就不需要传递IFacetsObject<T>作为参数。理想情况下,我可以将返回行更改为如下内容:

return T.ToObject(dt);

然后这样命名:

var result = ExecuteQuery<CMCGRGRGROUP>(sql).Take(5);

而不是像这样:

var result = ExecuteQuery<CMCGRGRGROUP>(sql, new CMCGRGRGROUP()).Take(5);

我承认我对泛型还不是很熟悉,所以在实现中可能有什么地方不对。

通用方法,其中T实现Interface<T>

您可以在ExecuteQuery方法上添加约束。你已经有了一个:要求T是可更新的。你可以这样声明它:

public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) 
  where T : IFacetsObject<T>, new()
{
    using (var conn = new AseConnection(_conn))
    {
        conn.Open();
        var cmd = new AseCommand(sql, conn);
        var dt = new DataTable();
        var da = new AseDataAdapter(sql, conn);
        da.Fill(dt);                
        return obj.ToObject(dt); //this is the interface method
    }
}

所以它现在知道T是一个IFacetsObject<T>。你现在可以这样做:

public IEnumerable<T> ExecuteQuery<T>(string sql) 
  where T : IFacetsObject<T>, new()
{
    using (var conn = new AseConnection(_conn))
    {
        conn.Open();
        var cmd = new AseCommand(sql, conn);
        var dt = new DataTable();
        var da = new AseDataAdapter(sql, conn);
        da.Fill(dt);                
        return new T().ToObject(dt); //this is the interface method
    }
}

在我看来还是挺难看的。

编辑反应:

注意不能调用T.ToObject——接口不能定义静态方法。解决方法是使用new来创建T的新实例并调用实例方法。

您需要在接口上使用通用约束。像这样声明:

public interface IFacetsObject<T> where T : IFacetsObject<T>
{
    IEnumerable<T> ToObject(DataTable obj);
}

为了让这个工作,你还必须改变你的声明,像这样:

public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) 
    where T : IFacetsObject<T>, new()