对象的成员是否应该自动设置在类中?

时间:2008-10-30 13:24:12

标签: c# properties constructor

当你有一个复杂的属性时,你应该实例化它还是留给用户实例化它?

例如(C#)

A)

 class Xyz{
     List<String> Names {get; set;}
 }

当我尝试使用时,我必须设置它。

...
Xyz xyz = new Xyz();
xyz.Name = new List<String>();
xyz.Name.Add("foo");
...

我好像修改了代码

B)

 class Xyz{
     public Xyz(){
         Names = new List<String>();
     }
     List<String> Names {get; }
 }

在这种情况下,我可以将List设为只读。

可能会出现另一种情况,我想你有意不想设置它。例如在

C)

 class Xyz{
     String Name {get; set;}
 }

我认为初始化是不好的做法。

这种情况是否有一些经验法则?

6 个答案:

答案 0 :(得分:3)

根据经验(意味着该规则总会有例外)在构造函数中设置您的成员。这就是构造函数的用途。

请记住,其中一个想法是OO是抽象的。要求'用户'(即想要在源代码中实例化对象的程序员)这样做是 违反抽象原则。在使用对象之前,用户必须考虑的另一件事是,这是另一个潜在的错误来源。

答案 1 :(得分:2)

这是我的正常解决方案:

class XYZ 
{
   public XYZ () { Names = new List<string>(); }
   public List<string> Names { get; private set; }
}

(请注意,它不适用于XmlSerialization,因为您需要在所有XmlSerialized属性上使用getter和setter。)(您可以覆盖它,但似乎需要付出太多努力)。

正如OregonGhost所指出的那样 - 您需要为此添加[XmlArray]才能使用XmlSerialization。

这仍然违反了封装规则,就像你想要完全正确一样,你会有:

class XYZ 
{
   public XYZ () { AllNames = new List<string>(); }
   private List<string> AllNames { get; set; }
   public void AddName ( string name ) { AllNames.Add(name); }
   public IEnumerable<string> Names { get { return AllNames.AsReadOnly(); } }
}

由于这违反了.Net框架几乎所有其余部分的设计,我通常最终会使用第一个解决方案。

但是,这确实有一个额外的好处,即XYZ可以跟踪对其名称集合的更改,并且XYZ是唯一可以修改名称集合的地方。

我已经针对少数情况实现了这一点,但是当我为所有事情做这件事时,它会导致与其他程序员的摩擦过多。

答案 2 :(得分:2)

是的,有经验法则。代码分析工具是一个良好的开端。

有关您的代码的一些经验法则:

允许setter收集属性的不良做法。这是因为它很容易处理空集合,就像代码中的完整集合一样。强迫人们对集合进行空检查会让你受到殴打。请考虑以下代码段:

public bool IsValid(object input){
  foreach(var validator in this.Validators)
    if(!validator.IsValid(input)
      return false;
  return true;
}

无论验证者的集合是否为空,此代码都有效。如果您希望验证,请将验证器添加到集合中。如果没有,请将集合留空。简单。允许集合属性为null会导致上面的恶臭代码版本:

public bool IsValid(object input){
  if(this.Validators == null) 
    return false;
  foreach(var validator in this.Validators)
    if(!validator.IsValid(input)
      return false;
  return true;
}

更多代码行,不那么优雅。

其次,对于除集合之外的参考类型,您必须在确定是否要设置属性值时考虑对象的行为方式。该属性是否有一个明显的默认值?或者属性的null值是否具有有效含义?

在您的示例中,您可能希望始终在setter中检查Name值,并在分配null时将其设置为默认值“(No name given)”。在将此对象与UI绑定时,这可能会更容易。或者,名称可能为null,因为您需要一个有效的名称,并且当调用者尝试对该对象执行操作而不先设置名称时,您将检查它并抛出InvalidOperationException。

与编程中的大多数事情一样,有很多不同的方法可以做某事,其中一半是坏的,另一半的每一种方式只在某些情况下才有效。

答案 3 :(得分:2)

构造函数的目的是构造对象。不需要进一步的“设置”。如果调用者知道某些信息,则应将其传递给构造函数:

<强>错误:

MyClass myc = new MyClass();
myc.SomeProp = 5;
myc.DoSomething();

从右:

MyClass myc = new MyClass(5);
myc.DoSomething();

如果需要为myc设置SomeProp以使其处于有效状态,则尤其如此。

答案 4 :(得分:1)

这真的取决于。

如果该类应该使用null属性正确运行,则可以将其保留为未初始化。否则,你最好在构造函数中初始化它。

并且,如果您不希望在施工后更改属性,则可以将其设为私有

class Xyz
{
     public Xyz(string name)
     {
         this.Name = name;
     }
     String Name 
     {
          get; 
          private set;
     }
}

答案 5 :(得分:0)

如果可能,您应该尝试封装数据。将对象公开为列表要求用户知道关于该类的太多私密细节。例如,他们必须知道他们是否必须从头开始创建列表以避免空引用异常。

public class Xyz
{
   private List<String> _names = new List<String>(); // could also set in constructor

   public IEnumerable<String> Names
   {
       get
       {
           return _names;
       }
   }

   public void AddName( string name )
   {
       _names.Add( name );
   }
}

现在使用List,HashTable,Dictionary等无所谓