C# 可空类型作为泛型参数可能吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/209160/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Nullable type as a generic parameter possible?
提问by Tom Pester
I want to do something like this :
我想做这样的事情:
myYear = record.GetValueOrNull<int?>("myYear"),
Notice the nullable type as the generic parameter.
请注意可空类型作为泛型参数。
Since the GetValueOrNull
function could return null my first attempt was this:
由于该GetValueOrNull
函数可以返回 null 我的第一次尝试是这样的:
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : class
{
object columnValue = reader[columnName];
if (!(columnValue is DBNull))
{
return (T)columnValue;
}
return null;
}
But the error I'm getting now is:
但我现在得到的错误是:
The type 'int?' must be a reference type in order to use it as parameter 'T' in the generic type or method
'int?' 类型 必须是引用类型才能将其用作泛型类型或方法中的参数“T”
Right! Nullable<int>
is a struct
! So I tried changing the class constraint to a struct
constraint (and as a side effect can't return null
any more):
对!Nullable<int>
是一个struct
!所以我尝试将类约束更改为struct
约束(并且作为副作用无法再返回null
):
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct
Now the assignment:
现在任务:
myYear = record.GetValueOrNull<int?>("myYear");
Gives the following error:
给出以下错误:
The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method
'int?' 类型 必须是不可为 null 的值类型才能将其用作泛型类型或方法中的参数“T”
Is specifying a nullable type as a generic parameter at all possible?
是否可能将可为空类型指定为泛型参数?
采纳答案by Greg Dean
Change the return type to Nullable, and call the method with the non nullable parameter
将返回类型更改为Nullable,并调用带有不可为空参数的方法
static void Main(string[] args)
{
int? i = GetValueOrNull<int>(null, string.Empty);
}
public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
{
object columnValue = reader[columnName];
if (!(columnValue is DBNull))
return (T)columnValue;
return null;
}
回答by Robert C. Barth
Just do two things to your original code – remove the where
constraint, and change the last return
from return null
to return default(T)
. This way you can return whatever type you want.
只需对原始代码做两件事 - 删除where
约束,并将最后一个return
从更改return null
为return default(T)
。这样你就可以返回任何你想要的类型。
By the way, you can avoid the use of is
by changing your if
statement to if (columnValue != DBNull.Value)
.
顺便说一句,您可以is
通过将if
语句更改为 来避免使用if (columnValue != DBNull.Value)
。
回答by Toby
Just had to do something incredible similar to this. My code:
只需要做一些与此类似的令人难以置信的事情。我的代码:
public T IsNull<T>(this object value, T nullAlterative)
{
if(value != DBNull.Value)
{
Type type = typeof(T);
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
{
type = Nullable.GetUnderlyingType(type);
}
return (T)(type.IsEnum ? Enum.ToObject(type, Convert.ToInt32(value)) :
Convert.ChangeType(value, type));
}
else
return nullAlternative;
}
回答by James Jones
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
object val = rdr[index];
if (!(val is DBNull))
return (T)val;
return default(T);
}
Just use it like this:
只需像这样使用它:
decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
string Unit = rdr.GetValueOrDefault<string>(2);
回答by Roland Roos
I think you want to handle Reference types and struct types. I use it to convert XML Element strings to a more typed type. You can remove the nullAlternative with reflection. The formatprovider is to handle the culture dependent '.' or ',' separator in e.g. decimals or ints and doubles. This may work:
我认为您想处理引用类型和结构类型。我使用它来将 XML 元素字符串转换为更类型化的类型。您可以使用反射删除 nullAlternative。formatprovider 用于处理依赖于文化的 '.' 或 ',' 分隔符,例如小数或整数和双精度数。这可能有效:
public T GetValueOrNull<T>(string strElementNameToSearchFor, IFormatProvider provider = null )
{
IFormatProvider theProvider = provider == null ? Provider : provider;
XElement elm = GetUniqueXElement(strElementNameToSearchFor);
if (elm == null)
{
object o = Activator.CreateInstance(typeof(T));
return (T)o;
}
else
{
try
{
Type type = typeof(T);
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
{
type = Nullable.GetUnderlyingType(type);
}
return (T)Convert.ChangeType(elm.Value, type, theProvider);
}
catch (Exception)
{
object o = Activator.CreateInstance(typeof(T));
return (T)o;
}
}
}
You can use it like this:
你可以这样使用它:
iRes = helper.GetValueOrNull<int?>("top_overrun_length");
Assert.AreEqual(100, iRes);
decimal? dRes = helper.GetValueOrNull<decimal?>("top_overrun_bend_degrees");
Assert.AreEqual(new Decimal(10.1), dRes);
String strRes = helper.GetValueOrNull<String>("top_overrun_bend_degrees");
Assert.AreEqual("10.1", strRes);
回答by Ian Kemp
Disclaimer:This answer works, but is intended for educational purposes only. :) James Jones' solution is probably the best here and certainly the one I'd go with.
免责声明:此答案有效,但仅用于教育目的。:) James Jones 的解决方案可能是这里最好的解决方案,当然也是我会采用的解决方案。
C# 4.0's dynamic
keyword makes this even easier, if less safe:
C# 4.0 的dynamic
关键字使这更容易,如果不那么安全:
public static dynamic GetNullableValue(this IDataRecord record, string columnName)
{
var val = reader[columnName];
return (val == DBNull.Value ? null : val);
}
Now you don't need the explicit type hinting on the RHS:
现在您不需要 RHS 上的显式类型提示:
int? value = myDataReader.GetNullableValue("MyColumnName");
In fact, you don't even need it at all!
事实上,你甚至根本不需要它!
var value = myDataReader.GetNullableValue("MyColumnName");
value
will now be an int, or a string, or whatever type was returned from the DB.
value
现在将是一个整数,或一个字符串,或从数据库返回的任何类型。
The only problem is that this does not prevent you from using non-nullable types on the LHS, in which case you'll get a rather nasty runtime exception like:
唯一的问题是,这不会阻止您在 LHS 上使用不可为空的类型,在这种情况下,您会得到一个相当讨厌的运行时异常,例如:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type
As with all code that uses dynamic
: caveat coder.
与所有使用dynamic
:caveat coder 的代码一样。
回答by Ryan Horch
This may be a dead thread, but I tend to use the following:
这可能是一个死线程,但我倾向于使用以下内容:
public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct
{
return reader[columnName] as T?;
}
回答by nurchi
I know this is old, but here is another solution:
我知道这很旧,但这是另一种解决方案:
public static bool GetValueOrDefault<T>(this SqlDataReader Reader, string ColumnName, out T Result)
{
try
{
object ColumnValue = Reader[ColumnName];
Result = (ColumnValue!=null && ColumnValue != DBNull.Value) ? (T)ColumnValue : default(T);
return ColumnValue!=null && ColumnValue != DBNull.Value;
}
catch
{
// Possibly an invalid cast?
return false;
}
}
Now, you don't care if T
was value or reference type. Only if the function returns true, you have a reasonable value from the database.
Usage:
现在,您不在乎T
是值类型还是引用类型。只有当函数返回 true 时,您才能从数据库中获得合理的值。用法:
...
decimal Quantity;
if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity))
{
// Do something with Quantity
}
This approach is very similar to int.TryParse("123", out MyInt);
这种方法非常类似于 int.TryParse("123", out MyInt);
回答by Hele
I just encountered the same problem myself.
我自己也遇到了同样的问题。
... = reader["myYear"] as int?;
works and is clean.
... = reader["myYear"] as int?;
工作并且很干净。
It works with any type without an issue. If the result is DBNull, it returns null as the conversion fails.
它适用于任何类型,没有问题。如果结果为 DBNull,则在转换失败时返回 null。
回答by Casey Plummer
Multiple generic constraints can't be combined in an OR fashion (less restrictive), only in an AND fashion (more restrictive). Meaning that one method can't handle both scenarios. The generic constraints also cannot be used to make a unique signature for the method, so you'd have to use 2 separate method names.
多个通用约束不能以 OR 方式(限制较少)组合,只能以 AND 方式(限制较多)组合。这意味着一种方法不能处理这两种情况。通用约束也不能用于为方法制作唯一签名,因此您必须使用 2 个单独的方法名称。
However, you can use the generic constraints to make sure that the methods are used correctly.
但是,您可以使用通用约束来确保正确使用这些方法。
In my case, I specifically wanted null to be returned, and never the default value of any possible value types. GetValueOrDefault = bad. GetValueOrNull = good.
就我而言,我特别希望返回 null,而不是任何可能的值类型的默认值。GetValueOrDefault = 不好。GetValueOrNull = 好。
I used the words "Null" and "Nullable" to distinguish between reference types and value types. And here is an example of a couple extension methods I wrote that compliments the FirstOrDefault method in System.Linq.Enumerable class.
我使用“Null”和“Nullable”这两个词来区分引用类型和值类型。这是我编写的几个扩展方法的示例,它们补充了 System.Linq.Enumerable 类中的 FirstOrDefault 方法。
public static TSource FirstOrNull<TSource>(this IEnumerable<TSource> source)
where TSource: class
{
if (source == null) return null;
var result = source.FirstOrDefault(); // Default for a class is null
return result;
}
public static TSource? FirstOrNullable<TSource>(this IEnumerable<TSource?> source)
where TSource : struct
{
if (source == null) return null;
var result = source.FirstOrDefault(); // Default for a nullable is null
return result;
}