1

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

例如(C#)

一个)

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

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

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

好像我在哪里修改代码

二)

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

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

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

C)

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

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

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

4

6 回答 6

3

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

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

于 2008-10-30T13:38:23.603 回答
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 是唯一可以修改名称集合的地方。

我已经在少数情况下实现了这一点,但是当我为所有事情都这样做时,它会引起与其他程序员的太多摩擦。

于 2008-10-30T13:36:07.930 回答
2

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

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

允许集合属性上的设置器是不好的做法。这是因为在代码中处理空集合就像处理完整集合一样简单。强迫人们对收藏品做空检查会让你被打败。考虑以下代码片段:

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;
}

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

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

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

就像编程中的大多数事情一样,有很多不同的方法可以做某事,其中一半是坏的,而另一半中的每一种方法都只在某些情况下是好的。

于 2008-10-30T13:39:14.320 回答
2

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

错误的:

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

正确的:

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

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

于 2008-10-30T14:28:50.040 回答
1

这真的取决于。

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

而且,如果您不想在施工后更改该属性,您可以将其私有化

class Xyz
{
     public Xyz(string name)
     {
         this.Name = name;
     }
     String Name 
     {
          get; 
          private set;
     }
}
于 2008-10-30T13:38:23.027 回答
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 等都没有关系。

于 2010-08-07T16:22:47.027 回答