Я осуществляю рефакторинг определенный код доступа к данным C# от предыдущего разработчика и любопытен на предмет шаблона, который он использовал.
Код первоначально выставил наборы (массивы) множества бизнес-объектов ActiveRecord-стиля - по существу возражает переносящимся полям базы данных. Я изменяю массивы на универсальные списки, но аспект кода, на предмет которого мне любопытно, - то, что предыдущий разработчик имел, Получают методы для каждого типа объекта, который он переносил, таким образом:
public Thing GetThing(int i) {
return things[i];
}
Существует несколько из этих методов, и я не могу ни за что в жизни думать ни о каком возможном преимуществе об использовании того механизма, просто относящегося к вещам [я] непосредственно. Давайте предположим для пользы аргумента, что вещами является общественная собственность, не общедоступное поле (в этом случае, это - на самом деле автореализованное свойство, так, чтобы предположение было на самом деле верно).
Я пропускаю что-то очевидное? Или даже что-то тайное?
ОБНОВЛЕНИЕ я должен, вероятно, разъяснить, что к этим наборам в настоящее время получают доступ из для циклов:
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 ().
Не знаю, очевидно ли это, но я думаю, что вы что-то упускаете.
Допустим, вещи
- это IList
. Затем, открыв его напрямую (как Things
), вызывающий код сможет вызывать Add
, Insert
, RemoveAt
и т. Д. Может быть, предыдущий разработчик не хотел этого допустить (и я уверен, что для этого есть много веских причин).
Даже если предположить, что это Вещь []
(поэтому Добавить
и т. Д. Будет недоступно), раскрытие его как такового все равно позволит вызывающему коду сделать что-то вроде obj.Things [0] = new Thing ();
, которая может быть операцией, которая не должна быть разрешена в зависимости от реализации класса.
Вы можете раскрыть Things
как ReadOnlyCollection
, который решит большинство этих проблем. Но все сводится к следующему: если разработчик только хочет разрешить вызывающему коду доступ к элементам по индексу - не более того, - то в качестве средства предоставляет единственный метод GetThing
. честно говоря, это имеет смысл.
Разумеется, есть и такая возможность: реализовать свойство this [int]
только с помощью метода доступа get
. Но это имеет смысл только в том случае, если рассматриваемый класс по существу является исключительно набором объектов Thing
(т. Е. Нет также набора некоторых других типов объекта, к которому вы хотите предоставить доступ внутри класса).
В общем, я думаю, что подход GetThing
довольно хорош.
Тем не менее, судя по тому, как вы сформулировали свой вопрос, похоже, что предыдущий разработчик принял другие довольно плохие решения:
вещи
непосредственно в качестве общедоступного свойства, ну, тогда ... это сводит на нет всю цель метода GetThing
, не так ли? Результатом является просто раздутый интерфейс (я обычно думаю, что это не лучший знак, когда у вас есть несколько методов для выполнения одного и того же, если только они явно не задокументированы как псевдонимы по какой-то уважительной причине). [ Обновление : похоже, что предыдущий разработчик , а не сделал это. Хорошо.] вещей
с помощью метода GetThing
, что просто глупо (на мой взгляд ). Зачем вводить бессмысленные накладные расходы на дополнительные вызовы методов с использованием открытого интерфейса класса из самого класса? Если вы в классе, вы уже внутри реализации и можете получить доступ к частным / защищенным данным сколько угодно - не нужно притворяться иначе. Если бы мне пришлось угадывать, я бы сказал, что этот разработчик использовал какой-то другой язык, например Java, и не был полностью осведомлен о стандартной практике в C #. Номенклатура «get [Property]» очень активно используется в Java, javascript и т. Д. C # заменяет ее свойствами и индексаторами. Свойства столь же мощны, как геттеры и сеттеры, но их проще писать и использовать. Единственный раз, когда вы обычно видите "Получить [что-то]" в C #, это если:
GetPrimeNumbers ()
), или GetRow (int i)
и GetColumn (int i))
. Даже в этом случае более распространено просто предоставить каждую из этих индексированных коллекций как собственное свойство, которое имеет индексированный тип (" table.Rows [2]
"). Если вы только получаете доступ к этим значениям в для
циклов, коллекция должна реализовывать IEnumerable
, что даст вам доступ к методам LINQ и конструкция foreach
. Если вам по-прежнему нужны индексированные геттеры, вам следует подумать об использовании собственного интерфейса, который расширяет IEnumerable
, но дополнительно предоставляет:
T this[int i] { get; }
Таким образом у потребителей не создается впечатление, что они могут Добавить
и Удалить
объектов в этой коллекции.
Обновление
Я знаю, что это в основном вопрос стиля, который является предметом споров, но я действительно думаю, что решение 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
Как указано в других ответах, это проблема ограничения доступа к самому массиву (или списку).
В общем, вы не хотите, чтобы клиенты этого класса могли напрямую изменять сам массив. Скрытие реализации базового списка также позволяет вам изменить реализацию в будущем, не затрагивая какой-либо клиентский код, который использует класс.
Здесь нужно отметить две вещи. Во-первых, вы хотите сохранить переменные объекта частными и использовать геттеры и сеттеры для доступа к ним. Это предотвращает случайное изменение или модификацию объектных переменных пользователем.
Во-вторых, считается хорошим соглашением об именовании, когда термины get/set должны использоваться при прямом доступе к атрибуту. Это помогает улучшить читабельность.
Хотя в данном случае это публичное свойство, использование геттеров и сеттеров улучшает читаемость и помогает предотвратить неожиданное поведение. Неважно, обращаетесь ли вы к переменным объекта в цикле, вы должны продолжать использовать соглашение GetThing
.
Наконец, если вы обращаетесь к переменным изнутри объекта, вам не нужно использовать getter или setter.
ПРИМЕЧАНИЕ: Обычно считается хорошим стилем держать переменные объекта приватными и использовать геттеры/сеттеры для всех языков
Вас также может заинтересовать вопрос "getter и setter для класса в классе c#"
Возможно, вы захотите представить объект как IList
. Это даст вам возможности индексирования, которые вы ищете, но вы также сможете использовать ряд функций LINQ, таких как создание нового списка элементов на основе условий. Плюс IList
реализует IEnumerable
, поэтому вы сможете использовать foreach
для циклического перебора объектов.
например. в вашем классе:
public IList<Thing> Things { get; private set; }
например. использование:
Thing x = business.Things[3];
или
var x = business.Things.Where(t => t.Name == "cori");