在属性内声明变量是否被认为是不良实践?

3

我有如下的类:

public class PeopleInfo
{
   public virtual int ID {get; protected set;}
   public virtual Person Person1 {get;set;}
   public virtual Person Person2 {get;set;}

   public virtual List<Person> People
   {
     get
     {
        var p = new List<Person>();
        p.Add(Person1);
        p.Add(Person2);
        return p;
     }
   }
}

我正在使用NHibernate。因为"PeopleInfo"表中每行有多个人,所以"Person"类被用作组件。People()属性的想法是提供一个只读列表,可以循环遍历。是否有更好的方法来实现这个,或者这个解决方案是否可接受?


请不要在标题前加上“C#”。这就是标签的作用。 - John Saunders
我认为这种做法没什么问题,只要确保你的get/set方法不会抛出异常。 - asawyer
除了 ObjectDisposedException 之外。 - Noldorin
6个回答

10
People属性的设计初衷是提供一个只读列表,可以循环遍历。是否有更好的方法来实现这一点或者这个解决方案被认为是可接受的呢?
如果这是你的意图,那么你没有实现它;你提供了一个可变的列表,可以循环遍历。
幸运的是,你每次都提供一个不同的可变列表,但你仍然提供了一个可变列表。
我倾向于提供一个不可变的列表。有许多方法可以做到这一点。如果你实际上提供了一个不可变的列表,那么你还有额外的好处,即列表可以进行惰性计算,然后缓存和无限期地重复使用,而不是每次请求时重新构建。
如果需要索引访问,则应创建一个ReadOnlyCollection并将其包装在列表的单个实例周围,然后缓存和重用只读集合。请注意,如果突变基础列表,则只读集合将出现突变;它只是一个只读列表,不是一个不可变列表。
如果不需要索引访问,则应通过返回IEnumerable而不是List来表示。然后,您可以返回任何您选择的不可变集合。

4
这篇回答做得相当不错,没有直接回答标题中的问题。 :-P 不过,楼主可能需要更清楚地表达他的问题... - Noldorin

4
如果它是只读的,你需要它成为List吗?你可以声明它为IEnumerable并执行以下操作:
public virtual IEnumerable<Person> People 
{
    get 
    {
         yield return Person1;
         yield return Person2;
    }
}

1
我最喜欢的编程特性!“yield”太棒了。一个选择是实现IEnumerable。 - Krumelur
你不能在属性上使用 readonly 修饰符,只能用于字段。 - svick

3

通常情况下,属性应该立即返回结果。从语义上讲,它们用于表示对象的某种“状态”。

你肯定不应该在属性中执行任何长时间计算。因此,它是否适合声明变量高度取决于上下文。一般情况下,这没有问题,但是动态生成列表可能由于其计算强度而不建议

MSDN还有一个对于开发人员有用的页面来考虑使用属性与方法之间的区别。简单总结一下:

总体上,方法表示操作,属性表示数据。


感谢反馈,您是否有另一种能够产生相同结果的解决方案?如果可能的话,我想要能够遍历每个“Person”。 - newkid91
@newkid91:我不确定你的具体情况。是否只有两个独立的“Person”对象? - Noldorin
如果只涉及两个对象,那么将任何逻辑重构为一个带有 Person 参数的方法,并在这两个 Person 对象上调用它。 - Noldorin

2

这种方法是否可接受,有更好的方法吗?

是的,有更好的方法:做对它。

public class PeopleInfo {
    public virtual int Id { get; set; }
    public virtual IList<Person> People { get; set; }
}

public class Person {
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual PeopleInfo PeopleInfo { get; set; }
}

public class PeopleInfoMap : ClassMap<PeopleInfo> {
    public PeopleInfoMap() {
        Id(x => x.Id);
        HasMany(x => x.People)
            .Cascade.None()
            .Inverse();
    }
}

public class PersonMap : ClassMap<Person> {
    public PersonMap() {
        Id(x => x.Id);
        Map(x => x.Name);
        References(x => x.PeopleInfo);
    }
}

您的数据库表应该如下所示:

PeopleInfo (Id PK)
Person (Id PK, Name, PeopleInfoId FK PeopleInfo.Id)

1

这段代码没有问题,但是每次调用get方法都会返回一个新的实例。我不确定这是否符合您的意图。


0

声明一个本地变量是可以的。然而,有一种方法可以缩短您的代码:

public virtual List<Person> People
{
    get
    {
        return new List<Person> { Person1, Person2 };
    }
}

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