2010-06-04 45 views
6

我有一些数据访问层代码调用存储过程并返回各种数据类型的标量值。语法是ExecuteDecimal,ExecuteString,等我希望它是Execute<string>Execute<decimal>如何让泛型能够使用需要谨慎铸造的返回值?

我尝试这个实现,我不能编译,除非我正在使用“(T)值”铸造,如果我尝试检查类型并调用一个方法来进行投射,没有这样的运气。

更新的问题 为什么我必须在转换为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); 
} 
+0

嘿我真的很担心。前三个答案(最高票数)或者有错误或者不需要代码。 – Andrey 2010-06-04 18:02:08

+0

@Andrey,欢迎来到SO =(我只是很失望,我没有去认识'精心铸造',当我读到标题时我非常兴奋=) – Marc 2010-06-04 20:13:05

回答

8

试试这个:

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处理

+0

有趣的是,它看起来像ChangeType仅支持支持IConvertable的事物。我以前从未见过这种技术。谢谢! – MatthewMartin 2010-06-04 17:36:01

+1

@MatthewMartin所有原始类型都支持它。但是这种方法并不好,因为max没有检查DBNull – Andrey 2010-06-04 17:53:10

+0

究竟谁会为它赞不绝口呢?它会在DBNull – Andrey 2010-06-04 17:59:38

1
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)); 
} 
+0

@Andrey--不会编译。 – dcp 2010-06-04 17:31:02

+0

你没有检查!但是,无论如何,它让我走上了正确的道路,我想我已经明白了。我用新代码更新了我的问题。 – MatthewMartin 2010-06-04 17:32:10

+0

啊看起来更好 – MatthewMartin 2010-06-04 17:33:33

1

试试“为”关键字

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; 
+1

Convert.ChangeType为你做所有的事情。并且您不检查DBNull – Andrey 2010-06-04 18:03:53

+2

否。'as'关键字对值类型无效。 – 2010-06-04 18:06:13

+0

谢谢乔尔,你说得对。现在好了 – paquetp 2010-06-04 22:59:48

0

也许你的功能可能反而被改变,以允许对象转换函数传递:

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.

+0

不幸的是,你不能在返回类型上重载,参数类型需要在类型和/或数量上有所不同。 – 2010-06-04 17:48:19

+0

有趣我以前从来没有见过T的默认(T),我想我会使用它。删除那些不会编译的部分,以便我可以提高您的答案。 – MatthewMartin 2010-06-04 17:54:36

+0

已将函数重命名以避免编译问题,因为我认为这是首选,而不是以无法解析调用者的类型为例。 – Reddog 2010-06-04 19:36:45