对象的任何优势.GetObject(i)over objects [i]?

时间:2010-07-01 15:03:13

标签: c# .net collections properties accessor

我正在重构前一位开发人员的一些C#数据访问代码,并对他使用的模式感到好奇。

代码最初暴露了各种ActiveRecord样式业务对象的集合(数组) - 本质上是包装数据库字段的对象。我正在将数组更改为通用列表,但我很好奇的代码方面是前一个开发人员为他正在包装的每种类型的对象都有Get方法,因此:

public Thing GetThing(int i) {
    return things[i];
}

这些方法中有几种,我不能为我的生活想到使用该机制而不是简单地直接引用事物[i]的任何可能的优势。为了论证,我们假设事物是公共财产,而不是公共领域(在这种情况下,它实际上是一个自动实现的属性,因此假设实际上是正确的。)

我错过了一些明显的东西吗?甚至是一些深奥的东西?

更新 我应该澄清一下,这些集合目前是从for循环中访问的:

for (int i = 0; i < thingsCount; i== ) {
    dosomthing( GetThing(i) );
    dosomethingelse( GetThing(i) );
}

我正在重构:

for (int i = 0; i < thingsCount; i== ) {
    Thing thing = things[i];
    dosomthing( thing );
    dosomethingelse( thing );
}

甚至可能使用things.foreach()。

5 个答案:

答案 0 :(得分:6)

我不知道这是否显而易见,但我认为你错过了什么。

我们说thingsIList<Thing>。然后直接公开它(Things)将允许调用代码来调用AddInsertRemoveAt等。也许以前的开发人员不想允许这样做(而且我确信这有很多充分的理由。)

即使假设它是Thing[](因此Add等也不可用),因此将其暴露仍然允许调用代码执行类似obj.Things[0] = new Thing();的操作,这可能是Things根据类的实现,不应该允许的操作。

可以ReadOnlyCollection<Thing>公开为GetThing,这将解决大部分问题。但它归结为:如果开发人员想要允许调用代码按索引访问项目 - 仅此而已 - 然后提供单个this[int]方法作为手段老实说,这样做是最有意义的。

现在,已经授予,还有这个选项:实现只有get访问者的Thing属性。但是,如果所讨论的类本质上只是GetThing个对象的集合(即,某些其他类型的集合),那么这才有意义您希望在类中提供访问权限的对象。)

总而言之,我认为things方法非常合理。

那就是说,从您提出问题的方式来看,这听起来像以前的开发人员做出了一些其他非常糟糕的决定:

  1. 如果他/她直接将GetThing集合作为公共财产公开,那么......那就违背了things方法的全部目的,没有是吗?结果只是一个膨胀的界面(我通常认为,当你有多种方法完成同样的事情时,它不是一个好兆头,除非出于某种正当理由将它们清楚地记录为别名)。 [更新:以前的开发人员似乎执行此操作。好。]
  2. 看起来前一位开发人员还内部使用GetThing方法访问{{1}}中的项目,这很愚蠢(在我看来)。为什么在类本身内使用类的公共接口引入额外方法调用的无意义开销?如果你在课堂上,你已经在实施中,并且可以访问所有你想要的私人/受保护数据 - 不需要假装。

答案 1 :(得分:2)

这里有两点需要注意。首先,您希望将对象变量保持为私有,并使用getter和setter来访问它们。这可以防止用户意外更改或修改对象变量。

其次,它被认为是一个很好的命名约定,其中必须在直接访问属性的地方使用术语get / set。这有助于提高可读性。

虽然在这种情况下它是公共属性,但使用getter和setter可提高可读性并有助于防止意外行为。如果要访问循环中的对象变量并不重要,则应继续使用GetThing约定。

最后,如果要从对象中访问变量,则不需要使用getter或setter。

注意:它通常被认为是保持对象变量私有的好方式,并且对所有语言使用getter / setter

C++ style guidelines

C# style guidelines

您可能也对“getter and setter for class in class c#

这个问题感兴趣

答案 2 :(得分:2)

您可能希望将对象公开为IList<Thing>。这将为您提供所需的索引功能,但您也可以使用LINQ函数的范围,例如根据条件创建新的项列表。加IList<T>实现IEnumerable<T>,这样您就可以使用foreach来遍历对象。

e.g。在你班上:

public IList<Thing> Things { get; private set; }

e.g。用法:

Thing x = business.Things[3];

var x = business.Things.Where(t => t.Name == "cori");

答案 3 :(得分:1)

如果我不得不猜测,我会说这个开发人员已经习惯了其他语言,比如Java,并且不完全了解C#中的标准做法。 “get [Property]”命名法在Java,javascript等中使用得非常频繁.C#将其替换为属性和索引器。属性与getter和setter一样强大,但更容易编写和使用。您通常在C#中看到“Get [something]”的唯一时间是:

  • 这项操作可能足够昂贵,你真的想把这不是简单的会员访问(例如GetPrimeNumbers())或
  • 带回家。
  • 您的收藏实际上包含多个索引收藏。 (例如GetRow(int i)GetColumn(int i))。即使在这种情况下,更常见的是将这些索引集合中的每一个作为属性暴露给自己,这是一个索引类型(“table.Rows[2]” )。

如果您for循环中访问这些值,则该集合应实现IEnumerable<Thing>,这将使您可以访问LINQ方法和foreach构造。如果您仍需要基于索引的getter,则应考虑使用自己的扩展IEnumerable<T>的接口,但另外提供:

T this[int i] { get; }

这样,您就不会给消费者留下他们可以在此集合中AddRemove个对象的印象。

<强>更新

我知道这主要是一种风格问题,需要辩论,但我认为GetThings解决方案不是正确的做法。以下策略虽然需要更多工作,但更符合标准.NET类和框架的设计方式:

public class ThingHolderDataAccess
{
    public ThingHolder GetThingHolderForSomeArgs(int arg1, int arg2)
    {
        var oneThings = GetOneThings(arg1);
        var otherThings = GetOtherThings(arg2);
        return new ThingHolder(oneThings, otherThings);
    }
    private IEnumerable<OneThing> GetOneThings(int arg)
    {
        //...
        return new List<OneThing>();
    }
    private IEnumerable<AnotherThing> GetOtherThings(int arg2)
    {
        //...
        return new List<AnotherThing>();
    }
}

public class ThingHolder
{
    public IIndexedReadonlyCollection<OneThing> OneThings
    {
        get;
        private set;
    }

    public IIndexedReadonlyCollection<AnotherThing> OtherThings
    {
        get;
        private set;
    }

    public ThingHolder(IEnumerable<OneThing> oneThings,
                       IEnumerable<AnotherThing> otherThings)
    {
        OneThings = oneThings.ToIndexedReadOnlyCollection();
        OtherThings = otherThings.ToIndexedReadOnlyCollection();
    }
}

#region These classes can be written once, and used everywhere
public class IndexedCollection<T> 
    : List<T>, IIndexedReadonlyCollection<T>
{
    public IndexedCollection(IEnumerable<T> items)
        : base(items)
    {
    }
}

public static class EnumerableExtensions
{
    public static IIndexedReadonlyCollection<T> ToIndexedReadOnlyCollection<T>(
        this IEnumerable<T> items)
    {
        return new IndexedCollection<T>(items);
    }
}

public interface IIndexedReadonlyCollection<out T> : IEnumerable<T>
{
    T this[int i] { get; }
}
#endregion

使用上面的代码可能如下所示:

var things = _thingHolderDataAccess.GetThingHolderForSomeArgs(a, b);
foreach (var oneThing in things.OneThings)
{
    // do something
}
foreach (var anotherThing in things.OtherThings)
{
    // do something else
}

var specialThing = things.OneThings[c];
// do something to special thing

答案 4 :(得分:0)

与其他答案一样,这是一个限制访问数组(或列表)本身的问题。

通常,您不希望类的客户端能够直接修改数组本身。隐藏底层列表的实现还允许您在将来更改实现,而不会影响使用该类的任何客户端代码。