可空日期时间转换

36

可能是重复的问题:
为什么可空int不能被赋予null值

我正在尝试将reader[3]对象(即datetime)转换为空,如果某个论坛没有lastPostDate,但是它显示我缺少一个转换。

由于<null>和'System.DateTime'之间没有隐式转换,无法确定条件表达式的类型。

public class Forums
{
    public List<Forum> GetForums()
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["CMS"].ConnectionString))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand("sproc_Forums_GetForums", conn);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default);

            List<Forum> forums = new List<Forum>();
            while (reader.Read())
            {
                var title = reader[6].ToString();
                var threadCount = (int)reader[5];
                var lastPostTitle = reader[4].ToString();
                // below is where im having a problem
                Nullable<DateTime> lastPostDate = (reader[3] == DBNull.Value ? null : Convert.ToDateTime(reader[3])); 
                var lastPostBy = reader[2].ToString();
                var forumGroup = reader[1].ToString();
                var description = reader[0].ToString();

                Forum forum = new Forum(0, "",DateTime.Now,
                    reader["Title"].ToString(),description,
                    0,false,"","",DateTime.Now,true,
                    forumGroup, (int)threadCount, lastPostBy,
                    lastPostDate, lastPostTitle);
                forums.Add(forum);/**/

            }
            return forums;

        }

    }            
}

下面是我的论坛类对象,其中包含一个可空的lastPostDate属性

    public class Forum
{
    public Forum(int forumID, string addedBy, DateTime addedDate, string title, string description, int parentID, bool moderated,
        string imageUrl, string updatedBy, DateTime? updatedDate, bool active, string forumGroup, int threadCount, string lastPostBy,
        Nullable<DateTime> lastPostDate, string lastPostTitle)
    {
        this.ForumID = forumID;
        this.AddedBy = addedBy;
        this.AddedDate = addedDate;
        this.Title = title;
        this.Description = description;
        this.ParentID = parentID;
        this.Moderated = moderated;
        this.ImageUrl = imageUrl;
        this.UpdatedBy = updatedBy;
        this.UpdatedDate = updatedDate;
        this.Active = active;
        this.ForumGroup = forumGroup;
        this.ThreadCount = threadCount;
        this.LastPostBy = lastPostBy;
        this.LastPostDate = lastPostDate;
        this.LastPostTitle = lastPostTitle;
    }

    private int _forumID;
    public int ForumID
    {
        get { return _forumID; }
        set { _forumID = value; }
    }
    private string _addedBy;
    public string AddedBy
    {
        get { return _addedBy; }
        set { _addedBy = value; }
    }
    private DateTime _addedDate = DateTime.Now;
    public DateTime AddedDate
    {
        get { return _addedDate; }
        set { _addedDate = value; }
    }
    private string _title = "";
    public string Title
    {
        get { return _title; }
        set { _title = value; }
    }
    private string _description = "";
    public string Description
    {
        get { return _description; }
        set { _description = value; }
    }
    private int _parentID = 0;
    public int ParentID
    {
        get { return _parentID; }
        set { _parentID = value; }
    }
    private bool _moderated = false;
    public bool Moderated
    {
        get { return _moderated; }
        set { _moderated = value; }
    }
    private string _imageUrl = "";
    public string ImageUrl
    {
        get { return _imageUrl; }
        set { _imageUrl = value; }
    }
    private string _updatedBy = "";
    public string UpdatedBy
    {
        get { return _updatedBy; }
        set { _updatedBy = value; }
    }
    private DateTime? _updatedDate = null;
    public DateTime? UpdatedDate
    {
        get { return _updatedDate; }
        set { _updatedDate = value; }
    }
    private bool _active = false;
    public bool Active
    {
        get { return _active; }
        set { _active = value; }
    }
    private string _forumGroup = "";
    public string ForumGroup
    {
        get { return _forumGroup; }
        set { _forumGroup = value; }
    }
    private int _threadCount = 0;
    public int ThreadCount
    {
        get { return _threadCount; }
        set { _threadCount = value; }
    }
    private string _lastPostBy = "";
    public string LastPostBy
    {
        get { return _lastPostBy; }
        set { _lastPostBy = value; }
    }
    private Nullable<DateTime> _lastPosteDate = null;
    public Nullable<DateTime> LastPostDate
    {
        get { return _lastPosteDate; }
        set { _lastPosteDate = value; }
    }
    private string _lastPostTitle = "";
    public string LastPostTitle
    {
        get { return _lastPostTitle; }
        set { _lastPostTitle = value; }
    }
}

2
提示:您可以使用DateTime?作为Nullable<DateTime>的简写,就像所有Nullable<>类型一样。例如,int?是Nullable<int>,Guid?是Nullable<Guid>等等。 - Roy Goode
另一个重复的问题:https://dev59.com/e3RA5IYBdhLWcg3wsgPq - phoog
1
请问您能否将与此问题无关的代码删除?您只需保留这一行:Nullable<DateTime> lastPostDate = (reader[3] == DBNull.Value ? null : Convert.ToDateTime(reader[3])); - gdoron
4个回答

47

你可能想这样做:

DateTime? lastPostDate =  (DateTime?)(reader.IsDbNull(3) ? null : reader[3]); 
你遇到的问题是三元运算符需要左右两边之间有一个有效的强制类型转换。而 null 无法转换为 DateTime。
请注意,上述代码之所以能够正常工作,是因为三元运算符两侧都是对象。该对象被明确地转换为 DateTime?,只要 reader[3] 确实是日期时间类型即可正常工作。

32

确保这两种类型都是可空的DateTime

var lastPostDate = reader[3] == DBNull.Value ?
                                        null : 
                                   (DateTime?) Convert.ToDateTime(reader[3]);
                                   
  • 使用 DateTime? 而不是 Nullable<DateTime> 可以节省时间...
  • 像我一样更好地缩进 ? 表达式。

我在 Eric Lippert 的 博客 中找到了这个很好的解释:

?: 操作符的规范如下所示:

?: 操作符的第二个和第三个操作数控制条件表达式的类型。设 X 和 Y 分别为第二个和第三个操作数的类型。然后,

  • 如果 X 和 Y 是相同的类型,则它们也是条件表达式的类型。
  • 否则,如果存在从 X 到 Y 的隐式转换,但不存在从 Y 到 X 的隐式转换,则Y 是条件表达式的类型。
  • 否则,如果存在从 Y 到 X 的隐式转换,但不存在从 X 到 Y 的隐式转换,则X 是条件表达式的类型。
  • 否则,无法确定任何表达式类型,并出现编译时错误。

编译器不会检查能够“容纳”这两种类型的类型。

在本例中:

  • nullDateTime 不是相同的类型。
  • null 没有隐式转换为 DateTime
  • DateTime 没有隐式转换为 null

因此,我们最终得到了一个编译时错误。


@KDM。我添加了Eric Lippert博客的解释。希望你会喜欢它。 - gdoron
优秀的解释! - Carlos Liu

8

将空值常量转换为: (DateTime?)null(Nullable<DateTime>)null

您也可以使用 default(DateTime?)default(Nullable<DateTime>)

而且,正如其他答案所指出的那样,您还可以将转换应用于 DateTime 值,而不是空字面量。

编辑(从我的评论中适应 Prutswonder 的答案):

重点是条件运算符不考虑其赋值目标的类型,因此它只会在第二个操作数的类型与第三个操作数的类型之间存在隐式转换,或者第三个操作数的类型与第二个操作数的类型之间存在隐式转换时编译。

例如,以下代码将无法编译:

bool b = GetSomeBooleanValue();
object o = b ? "Forty-two" : 42;

将第二个或第三个操作数转换为object可以解决问题,因为从int到object和从string到object都有隐式转换:
object o = b ? "Forty-two" : (object)42;

或者

object o = b ? (object)"Forty-two" : 42;

我不确定为什么有人会踩这个答案,就我所知它是正确的。 - user743382

6
你可以尝试这个。
var lastPostDate = reader[3] == DBNull.Value ?
                                default(DateTime?): 
                                Convert.ToDateTime(reader[3]);

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