【问题标题】:How do I get generics to work with return values that require careful casting?如何让泛型与需要仔细转换的返回值一起工作?
【发布时间】:2010-06-04 17:25:07
【问题描述】:

我有一些数据访问层代码调用存储过程并返回各种数据类型的标量值。语法是 ExecuteDecimal、ExecuteString 等。我希望它是 Execute<string>Execute<decimal>

我尝试了这个实现,但我无法编译,除非我使用“(T)值”进行强制转换,如果我尝试检查类型并调用方法进行强制转换,没有这样的运气。

更新问题 为什么我必须先转换为object才能转换为T?

更新代码

internal T Execute<T>(string storedProcName, Hashtable parameters)
{
      //Next lines do compile (thanks to suggestions from answers!)
      if (typeof(T) == typeof(string))
          return (T) (object) ExecuteScalar(storedProcName, parameters).ToString();
      else if (typeof(T) == typeof(int))
          return (T)(object) Convert.ToInt32(ExecuteScalar(storedProcName, parameters));
      //Next line compiles, but not all things boxed in an object can
      //be converted straight to type T (esp db return values that might contain DbNull.Value, etc)
      return (T)ExecuteScalar(storedProcName, parameters);
}

【问题讨论】:

  • 嘿,我真的很担心。前三个答案(得票最高)要么有错误,要么包含不需​​要的代码。
  • @Andrey,欢迎来到 SO =( 我很失望我没能了解“小心选角”。当我读到标题时,我非常兴奋。=)

标签: c# .net generics casting


【解决方案1】:

试试这个:

var result = ExecuteScalar(storedProcName, parameters);
if(Convert.IsDBNull(result))
    return default(T);
if(result is T) // just unbox
    return (T)result;
else            // convert
    return (T)Convert.ChangeType(result, typeof(T));

更新:修复 DBNull 处理

【讨论】:

  • 有趣,看起来 ChangeType 只支持支持 IConvertable 的东西。我以前从未见过这种技术。谢谢!
  • @MatthewMartin 所有原始类型都支持它。但是这个方法不好,因为max没有检查DBNull
  • 现在好多了!取消投票
  • 如果您将可空结果转换为其他类型(例如 int?->uint?无法更改类型
【解决方案2】:
typeof(T) == typeof(string)

以及针对DBNull.Value的空值检查

整体方法:

internal T Execute<T>(string storedProcName, Hashtable parameters)
{
      object res = ExecuteScalar(storedProcName, parameters);
      if (Convert.IsDBNull(res))
         return default(T); //or handle somehow
      return (T)Convert.ChangeType(res, typeof(T));
}

【讨论】:

  • @Andrey - 这不会编译。
  • 你没有检查!但无论如何,谢谢,它让我走上了正确的道路,我想我明白了。我已经用新代码更新了我的问题。
  • 实际上,我确实在编译器中检查了您的代码,看看您的方式是否可行,因为它绝对比 typeof(T) 更干净。但遗憾的是,这是必需的。删除了反对票:)。
  • @dcp 我总是混淆那些没有 IDE 的类型比较
  • @MatthewMartin 检查编辑。我没有手动 IDE 来检查它是否有效,但我认为它应该 :)
【解决方案3】:

试试 'as' 关键字

object o = ExecuteScalar(storedProcName, parameters);
string s;
int i;
// .. more

if ((s = o as string) != null)
{
  return s;
}
if ((i = o as int?) != null) // can't use as for value types, so use nullable
{
  return Convert.ToInt32(o);
}
return o as T;

【讨论】:

  • Convert.ChangeType 为您完成所有工作。并且您不检查 DBNull
  • 没有。 “as”关键字对值类型无效。
【解决方案4】:

也许您的函数可能会被更改以允许传入对象转换函数:

internal T Execute<T>(string storedProcName, Hashtable parameters, Func<object, T> resultConverter)
{
}

然后你可以为你知道的那些创建重载:

internal string ExecuteForString(string storedProcName, Hashtable parameters)
{
    return Execute(storedProcName, parameters, (o) => o.ToString());
}

internal int ExecuteForInt(string storedProcName, Hashtable parameters)
{
     return Execute(storedProcName, parameters, Convert.ToInt32);
}

在您的 DBNull 检查的情况下,您可以返回 default(T),或者如果返回 DBNull,您可以添加另一个重载以传递默认值 - 例如在 int 的情况下,返回 -1 而不是 0 可能会更好。

【讨论】:

  • 不幸的是,您不能重载返回类型,参数类型需要在类型和/或数量上有所不同。
  • 有趣的是,在我认为我会使用它之前,我从未见过默认的 T (T)。删除无法编译的部分,以便我支持您的答案。
  • 已重命名函数以避免编译问题,因为我认为这是首选而不是最终导致您无法为调用者解决的类型的情况......
猜你喜欢
  • 2019-02-26
  • 2021-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-29
  • 1970-01-01
  • 2023-02-02
相关资源
最近更新 更多