在C#中使用默认的通用值

3

我有一些用C#编写的代码。我正在尝试编写一些通用代码,以便可以重复使用。我的实际代码更加复杂。不过,需要处理的代码看起来像下面这样:

public async Task<T> ExecuteQueryAsync<T>(string sql)
{
  var results = default(T);
  using (var database = new SqlConnection("[ConnectionString]"))
  {
    database.Open();
    results = await database.QueryAsync<T>(sql);                        
  }
  return results;
}

当我构建我的项目时,出现一个编译时错误,错误信息如下:

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<T>' to 'T'. An explicit conversion exists (are you missing a cast?)

我不完全理解为什么会出现这个错误。我打算以以下方式进行调用:

Adapter adapter = new Adapter();
var results = await adapter.ExecuteQueryAsync<IEnumerable<Customer>>("SELECT * FROM Customer");

在上述情况下,T 不应该是一个 IEnumerable<T> 吗?如果是这样,我不明白为什么在编译时会出现类型转换错误。谢谢!

3
results 的类型是 T,而 database.QueryAsync<T>(sql) 返回的类型是 IEnumerable<T>。只需将结果类型更改为 IEnumerable<T>,我认为这才是您真正想要的。默认值与您的问题无关。 - Andrey
1
我知道这是简化的代码,但为了确保...你真的需要向这个代码添加一个机制,以便单独接受SQL参数数据而不是SQL命令字符串。否则,你几乎是在强迫自己编写极其不安全的代码。 - Joel Coehoorn
3个回答

5
尝试像这样做。
public async Task<IEnumerable<T>> ExecuteQueryAsync<T>(string sql)
{
  IEnumerable<T> results = Enumerable.Empty<T>();
  using (var database = new SqlConnection("[ConnectionString]"))
  {
    database.Open();
    results = await database.QueryAsync<T>(sql);                        
  }
  return results;
}

1
我认为很容易 return await database.QueryAsync<T>(sql);,没有任何变量。 - Grundy
等同于“true”的内容。 - Daniel A. White
1
@Grundy 实际上,这个值被证明是不可能返回的,因此使用 null 只是避免得到一个空序列。 - Servy
2
@Grundy 但实际上它并不会返回 null,即使你使用了 null。再次强调,它是一个你可以证明永远不会被返回的值;它是什么并不重要。如果你愿意,甚至可以不初始化它并且不给它赋任何值 - Servy
1
是的,@Servy 是正确的。在 using 语句之前不初始化它要好得多。因此,在声明时只需使用 IEnumerable<T> results; 而不进行任何赋值。将其设置为任何不会被使用的内容(无论是 null 还是某个空对象)都没有意义。编译器对这种方法很满意。 - Jeppe Stig Nielsen
显示剩余3条评论

3

在您的ExecuteQueryAsync中,不需要使用results变量,因为您总是重写它。因此,您的函数可以如下所示:

public async Task<IEnumerable<T>> ExecuteQueryAsync<T>(string sql)
{
    using (var database = new SqlConnection("[ConnectionString]"))
    {
        database.Open();
        return await database.QueryAsync<T>(sql);                        
    }
}

1

这里已经有很好的答案了,但我想确保我们有一个支持安全查询参数化的答案。原始代码强制你编写极其不安全的代码,这将导致你的应用程序被黑客攻击。这使你可以避免这种情况:

public async Task<IEnumerable<T>> ExecuteQueryAsync<T>(string sql, dynamic Parameters = null)
{
  IEnumerable<T> results;
  using (var database = new SqlConnection("[ConnectionString]"))
  {
    database.Open();
    results = await database.QueryAsync<T>(sql, Parameters);                        
  }
  return results;
}

然后你可以这样调用它:
Adapter adapter = new Adapter();
var results = await adapter.ExecuteQueryAsync<IEnumerable<Customer>>(
        "SELECT * FROM Customer WHERE Sales >= @Sales", 
        new {Sales = 500.0m});

1
我认为这与@Daniel的答案相同,或者我错过了什么? - Grundy
@Grundy 现在有一个如何调用它的示例。 - Joel Coehoorn
1
感谢您删除了无意义的初始化= Enumerable.Empty<T>(),这是您最初从另一个答案中复制粘贴的(请参见Daniel A. White的答案中的评论)。 - Jeppe Stig Nielsen
1
@JeppeStigNielsen 为什么要让它保持未初始化状态,当你可以轻松地在一开始就消除任何需要。这样做可以显著提高代码的可读性。 - Servy
1
@Servy 因为问题中有“我的实际代码更复杂”的说法。可能还有其他情况需要在该方法中使用该变量。 - Joel Coehoorn
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接