我正在重构前一位开发人员的一些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()。
答案 0 :(得分:6)
我不知道这是否显而易见,但我认为你错过了什么。
我们说things
是IList<Thing>
。然后直接公开它(Things
)将允许调用代码来调用Add
,Insert
,RemoveAt
等。也许以前的开发人员不想允许这样做(而且我确信这有很多充分的理由。)
即使假设它是Thing[]
(因此Add
等也不可用),因此将其暴露仍然允许调用代码执行类似obj.Things[0] = new Thing();
的操作,这可能是Things
根据类的实现,不应该允许的操作。
你可以将ReadOnlyCollection<Thing>
公开为GetThing
,这将解决大部分问题。但它归结为:如果开发人员仅想要允许调用代码按索引访问项目 - 仅此而已 - 然后提供单个this[int]
方法作为手段老实说,这样做是最有意义的。
现在,已经授予,还有这个选项:实现只有get
访问者的Thing
属性。但是,如果所讨论的类本质上只是GetThing
个对象的集合(即,还某些其他类型的集合),那么这才有意义您希望在类中提供访问权限的对象。)
总而言之,我认为things
方法非常合理。
那就是说,从您提出问题的方式来看,这听起来像以前的开发人员做出了一些其他非常糟糕的决定:
GetThing
集合作为公共财产公开,那么......那就违背了things
方法的全部目的,没有是吗?结果只是一个膨胀的界面(我通常认为,当你有多种方法完成同样的事情时,它不是一个好兆头,除非出于某种正当理由将它们清楚地记录为别名)。 [更新:以前的开发人员似乎不执行此操作。好。] GetThing
方法访问{{1}}中的项目,这很愚蠢(在我看来)。为什么在类本身内使用类的公共接口引入额外方法调用的无意义开销?如果你在课堂上,你已经在实施中,并且可以访问所有你想要的私人/受保护数据 - 不需要假装。答案 1 :(得分:2)
这里有两点需要注意。首先,您希望将对象变量保持为私有,并使用getter和setter来访问它们。这可以防止用户意外更改或修改对象变量。
其次,它被认为是一个很好的命名约定,其中必须在直接访问属性的地方使用术语get / set。这有助于提高可读性。
虽然在这种情况下它是公共属性,但使用getter和setter可提高可读性并有助于防止意外行为。如果要访问循环中的对象变量并不重要,则应继续使用GetThing
约定。
最后,如果要从对象中访问变量,则不需要使用getter或setter。
注意:它通常被认为是保持对象变量私有的好方式,并且对所有语言使用getter / setter
这个问题感兴趣答案 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; }
这样,您就不会给消费者留下他们可以在此集合中Add
和Remove
个对象的印象。
<强>更新强>
我知道这主要是一种风格问题,需要辩论,但我认为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)
与其他答案一样,这是一个限制访问数组(或列表)本身的问题。
通常,您不希望类的客户端能够直接修改数组本身。隐藏底层列表的实现还允许您在将来更改实现,而不会影响使用该类的任何客户端代码。