【问题标题】:Should I make my object properties nullable or use the CLR default values for each type?我应该让我的对象属性为空还是为每种类型使用 CLR 默认值?
【发布时间】:2011-09-23 16:55:10
【问题描述】:

我一直在尝试找出处理默认值的最佳方法。将 ID 值设置为 0 是有意义的,但如果它是货币值或其他最初未设置的值,当您稍后在代码中遇到它时,无法判断它是否已设置或设置为 0。如果货币值设置为空,那么你知道它没有被设置。此外,在处理数据库时,很容易知道是否需要将空值写入字段,而不是试图弄清楚它是否应该为空。

处理此问题的公认方法是什么?

Class MyModel
{
    public int Id {get;set;}
    public string Title {get;set;}
    public DateTime CreatedDate {get;set;}
    public bool IsActive {get;set;}

    //CLR will automatically set these values
    Public MyModel()
    {
        Id = 0; 
        Title = String.Empty;
        CreatedDate = "1/1/0001";
        IsActive = false;
    }
}

Class MyModel
{
    public int? Id {get;set;}
    public string Title {get;set;}
    public DateTime? CreatedDate {get;set;}
    public bool? IsActive {get;set;}

    //CLR will automatically set these values
    Public MyModel()
    {
        Id = null; 
        Title = null;
        CreatedDate = null;
        IsActive = null;
    }
}

【问题讨论】:

    标签: c# asp.net architecture


    【解决方案1】:

    您可以随时混合使用方法,具体取决于您的领域。

    static class ID
    {
        public const int Unassigned = -1;
    }
    
    class MyModel
    {
        public int Id { get; private set; }
        public string Title { get; set; }
        public DateTime CreatedDate { get; set; }
        public bool IsActive { get; set; }
        public bool? IsAwesome { get; set; }
    
        Model () 
        {
            // you may use "default" constants...
            Id = ID.Unassigned;
        }
    
        // you may use optional parameters or overloads
        public MyModel (string title,
            DateTime created = DateTime.Now, // default values may be concrete 
            bool? isAwesome = null)          // or nullable as well
            : this ()                        // and you can chain-call constructors!
        {
            Title = title ?? "<no title>";   // you don't always want null to come through
            CreatedDate = created;     
            IsAwesome = isAwesome;    
        }
    }
    
    // Possible usages:
    var model = new MyModel ("Hello", new DateTime (2001, 1, 1));
    var model = new MyModel ("world", isAwesome: true);
    var model = new MyModel (null) {
        IsActive = true
    };
    

    某些属性具有null 值可能是有意义的,例如“未设置”。

    在您的示例中,模型在持久化到数据库之前可能真的没有Id。如果是这种情况,并且无 id 模型在业务逻辑方面有意义,则可以为空的 IdId = 0 更好。但是,如果您的应用程序从不使用无 id 模型,并且通常期望 Id 等于某个值,那么编写它会很疯狂

    if (model.Id != null)
    

    每次你想用它做点什么。

    在这种情况下,您可能应该默认使用Id = 0
    你也可以引入一个常量(就像我在上面所做的那样),尽管我不推荐它用于除了 id 之外的任何东西,并且只有在它们被其他地方的代码大量使用的情况下。

    同样,一切都取决于域。
    您的工作是确保不能轻易创建违反业务规则的对象。

    【讨论】:

    • 很好的答案...在阅读之前我已经开始朝这个方向前进了。
    【解决方案2】:

    公认的处理方法是什么?

    接受了吗?这取决于域。

    如果将货币值设置为 null,则您知道它尚未设置。

    不一定。想象一下,我正在使用一个银行应用程序,我想搜索一个特定的交易,我得到一个如下所示的对话框:

    Enter the fields you know:
    Transaction date: 6/26/2011
    Payee: Apple
    Amount:
    

    现在TransactionSearchParameters.Amount 应该设置为空。您无法将其与未设置区分开来。

    此外,在处理数据库时,很容易知道是否需要将空值写入字段而不是试图弄清楚它是否应该为空。

    您应该花更多时间正确地为您的域建模,然后让 ORM 弄清楚如何将这些东西正确地放入数据库。

    【讨论】:

    • 所以您建议不要设置它们,除非您需要它们不是 CLR 默认值,或者您是否建议将它们显式设置为某些默认值?
    【解决方案3】:

    只是在这里添加一个想法,在适合使用时,总是有复杂类型的null object pattern

    【讨论】:

      【解决方案4】:

      这取决于您将如何使用您的代码。如果属性是简单类型,如整数和字符串,默认值更好,如果属性本身是对象,我使用空值,因为在 C#/Java/PHP 中,对象引用实际上是对象指针,更好用。

      但是,如果您的属性是集合,例如 lists 或 maps ,那么创建集合并将其留空而不是 null 是一种“更好的做法”。

      干杯。

      【讨论】:

        【解决方案5】:

        嗯......“最好”的答案(在那里非常固执,但我有权)是当你不需要区分两者时,因为只有一种可能的状态,即有效:

        class MyModel
        {
            public int Id {get; private set;}
            public string Title {get; private set;}
            public DateTime CreatedDate {get; private set;}
            public bool IsActive {get; private set;}
        
            Public MyModel(int Id, string Title, DateTime CreatedDate, bool IsActive)
            {
                this.Id = Id; 
                this.Title = Title;
                this.CreatedDate = CreatedDate;
                this.IsActive = IsActive;
            }
        }
        

        我知道这并不总是可行的,例如 Query by Example。

        通常会避免使用“将 ID 值设置为 0 有意义...”之类的“幻数”。一旦你现在这样做了,你编写的每一段代码都必须编码,以了解这些幻数是什么以及它们的含义。这是很少实现的,并且充满了令人讨厌的和容易出错的代码。

        如果您只是必须区分具有值和不具有值的字段,那么您后面的示例会好一些。至少在这里你的值类型是明确有效或无效的。但是,使用 Nullable 意味着您必须使用不同的方法来确定类、字符串或其他引用类型是否无效。

        IMO,您最好使用以下内容,尽管您将看到它变得非常冗长。

        class MyModel
        {
            private int _id;
            public bool HasId { get; set; }
            public int Id
            {
                get
                {
                    if (!HasId) throw new System.InvalidOperationException();
                    return _id;
                }
                set
                {
                    HasId = true;
                    _id = value;
                }
            }
        
            private string _title;
            public bool HasTitle { get; set; }
            public string Title
            {
                get
                {
                    if (!HasTitle) throw new System.InvalidOperationException();
                    return _title;
                }
                set
                {
                    if (value == null) throw new System.ArgumentNullException("Title");
                    HasTitle = true;
                    _title = value;
                }
            }
        
            private DateTime _createdDate;
            public bool HasCreatedDate { get; set; }
            public DateTime CreatedDate
            {
                get
                {
                    if (!HasCreatedDate) throw new System.InvalidOperationException();
                    return _createdDate;
                }
                set
                {
                    HasCreatedDate = true;
                    _createdDate = value;
                }
            }
        
            private bool _isActive;
            public bool HasIsActive { get; set; }
            public bool IsActive
            {
                get
                {
                    if (!HasIsActive) throw new System.InvalidOperationException();
                    return _isActive;
                }
                set
                {
                    HasIsActive = true;
                    _isActive = value;
                }
            }
        }
        

        最后,如果你要走这条路,代码生成器会很好地为你服务。

        【讨论】:

        • 我想为第一部分投票 +1 就像为第二部分投票 -1 一样。
        • 展开,第二部分是可空类型问题的典型示例已解决及其简洁的语法。当我想到像 public bool? IsActive { get; set; } 这样简单的事情得到的行数超出其应有的 15 倍 时,我的头很痛。是的——这个技巧不适用于字符串,但那里有 null 非常适合这个目的。
        • @gaearon 同意后一种情况,正如我警告的那样,它很冗长,而且我从来没有那样手写代码。然而,我已经在代码生成器中做到了。使用“null”仍然是一个“神奇”值,必须由消费者检查或由“getter/setter”方法断言。如果您在 getter/setter 中断言非空值,那么您将回到后面示例中的几乎每一行代码。此外,您仍然需要公开布尔值“HasWhatever”以查看是否提供了值,因此最终使用 'null' 和 Nullable 在 LoC 方面没有任何好处。
        • (续)我鄙视 null 以及所有可以返回它的属性。应该避免或防止它。如果没有其他方法,请使用 TryGetValue(...) 模式而不是返回 null。它至少将逻辑保持在内部,并且将属性公开为它们的类型(int)而不是可为空的类型(int?)。
        • 我已决定在大多数 的情况下坚持使用默认值,并在需要时强制为空。您的第一个示例很好,但是在某些情况下,我可能需要对对象进行部分水合,并且对我而言,只有当您尝试执行需要规则的操作时,如果它不符合业务规则,对象才会真正无效。例如 - 对象的新实例在保存到数据库之前可能没有 Id。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-26
        • 2016-10-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-18
        相关资源
        最近更新 更多