无法在泛型方法中将类型更改为可为空

Posted

技术标签:

【中文标题】无法在泛型方法中将类型更改为可为空【英文标题】:Cannot change type to nullable in generic method 【发布时间】:2012-05-06 07:05:11 【问题描述】:

我正在创建一个通用转换器

这是通用转换器的示例代码

bool TryReaderParse<TType>(object data, out TType value)

    value = default(TType);
    Type returnType = typeof(TType);
    object tmpValue = null;

    if (returnType == typeof(DateTime))
    
        tmpValue = StringToDatetime(data.ToString());
    
    else if (returnType == typeof(DateTime?)) // THIS IF FIRES
    
        tmpValue = StringToNullableDatetime(data.ToString());
    

    value = (TType)Convert.ChangeType(tmpValue, returnType);  // THROWS


public DateTime? StringToNullableDatetime(string date)

    DateTime? datetime = null;
    if (!string.IsNullOrEmpty(date))
    
        datetime = DateTime.Parse(date, new CultureInfo(Resources.CurrentCulture));
    

    return datetime;

这就是我使用它的方式:

void foo()

    DateTime? date = null;
    TryReaderParse<DateTime?>("25/12/2012", out date);

抛出的异常表示它无法从DateTime 转换为Nullable&lt;DateTime&gt;。既然该方法创建并返回一个可为空的类型,那么为什么转换失败了?

最后,在这个特定的示例中,我希望有一个可为空的 DateTime。

edit 问题是StringToNullableDatetime 方法返回一个Datetime? 并且强制转换说不能从Datetime 转换

既然StringToNullableDatetime方法返回一个可以为空的日期时间,那Convert.ChangeType怎么可能看不到传递的参数可以为空呢?

附言。我读过类似this 的答案,相反的答案(从可空值转换)。

【问题讨论】:

【参考方案1】:

抛出的异常表示它无法从DateTime 转换为Nullable&lt;DateTime&gt;。既然方法创建并返回了一个可以为空的类型,那么为什么转换会失败呢?

好问题。这失败了,因为不存在盒装可为空的东西。当您将DateTime? 转换为object 时,如果DateTime? 为空,您将获得一个空引用,或者您将获得一个装箱的,即DateTime。你永远不会得到一个盒装的可为空结构;没有这样的东西。

因此,您最终会在该框中得到 null 或有效的 DateTime。然后,您告诉 Convert 将其转换为可为空的 DateTime,而 Convert 不知道该怎么做。

我的建议是你完全放弃这条攻击线;这段代码是对泛型的滥用。每当您切换特定类型的泛型时,您的代码就不再是 generic 并且您可能做错了。如果你想为日期时间做一个“尝试”风格的方法,那么只需写下:

DateTime? TryReadDateTime(object data)

    ... return null if the object cannot be read as a datetime ...

为您打算阅读的每个类型编写这样的方法。用户更愿意写:

DateTime? d = TryReadDateTime(data);
if (d != null) ...

DateTime d;
bool b = TryRead<DateTime>(data, out d);
if (b) ...

【讨论】:

【参考方案2】:

来自documentation,如果出现以下情况,此行将出错:

value 为 null,conversionType 为值类型

Nullable&lt;T&gt; 是一个结构体,因此是一个值类型,因此如果您的值为 null,则不能使用此方法调用。您已经单独处理日期,那么为什么还要在这些情况下使用ChangeType

【讨论】:

编辑了我的问题。我的问题是我无法返回 Nullable 日期时间。 Convert.ChangeType 行看不到传递的参数可以为空【参考方案3】:

可空值、泛型和装箱的交互方式很奇怪。您最好定义两种方法:

bool TryReaderParse(object data, out TType value); bool TryReaderParse(object data, out TType? value) where TType : struct;

在第二种方法中,您的代码可以简单地生成TType 并将其分配给TType? 毫无困难。

【讨论】:

首先,一个方法不能仅在约束上重载。其次,如果该方法返回一个可为空的值,那么为什么它还需要返回一个布尔值?如果您要这样做,那么正确的签名是T TryParseClass&lt;T&gt;(object data) where T : classT? TryParseStruct&lt;T&gt;(object data) where T : struct。不需要布尔值。

以上是关于无法在泛型方法中将类型更改为可为空的主要内容,如果未能解决你的问题,请参考以下文章

在泛型方法中处理类型创建

在泛型方法中返回原始集合类型

通过在运行时在泛型方法中进行类型转换来使用递归函数

涉及泛型对象的泛型属性的赋值无法在泛型函数中正确进行类型检查

如何添加类型约束以在泛型方法中包含任何可序列化的内容?

java在泛型方法中使用它(继承)