Любое преимущество для объектов. GetObject (i) по объектам [я]?

Я осуществляю рефакторинг определенный код доступа к данным 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 ().

6
задан cori 2 July 2010 в 04:09
поделиться

5 ответов

Не знаю, очевидно ли это, но я думаю, что вы что-то упускаете.

Допустим, вещи - это IList . Затем, открыв его напрямую (как Things ), вызывающий код сможет вызывать Add , Insert , RemoveAt и т. Д. Может быть, предыдущий разработчик не хотел этого допустить (и я уверен, что для этого есть много веских причин).

Даже если предположить, что это Вещь [] (поэтому Добавить и т. Д. Будет недоступно), раскрытие его как такового все равно позволит вызывающему коду сделать что-то вроде obj.Things [0] = new Thing (); , которая может быть операцией, которая не должна быть разрешена в зависимости от реализации класса.

Вы можете раскрыть Things как ReadOnlyCollection , который решит большинство этих проблем. Но все сводится к следующему: если разработчик только хочет разрешить вызывающему коду доступ к элементам по индексу - не более того, - то в качестве средства предоставляет единственный метод GetThing . честно говоря, это имеет смысл.

Разумеется, есть и такая возможность: реализовать свойство this [int] только с помощью метода доступа get . Но это имеет смысл только в том случае, если рассматриваемый класс по существу является исключительно набором объектов Thing (т. Е. Нет также набора некоторых других типов объекта, к которому вы хотите предоставить доступ внутри класса).

В общем, я думаю, что подход GetThing довольно хорош.

Тем не менее, судя по тому, как вы сформулировали свой вопрос, похоже, что предыдущий разработчик принял другие довольно плохие решения:

  1. Если он / она также раскрыл вещи непосредственно в качестве общедоступного свойства, ну, тогда ... это сводит на нет всю цель метода GetThing , не так ли? Результатом является просто раздутый интерфейс (я обычно думаю, что это не лучший знак, когда у вас есть несколько методов для выполнения одного и того же, если только они явно не задокументированы как псевдонимы по какой-то уважительной причине). [ Обновление : похоже, что предыдущий разработчик , а не сделал это. Хорошо.]
  2. Похоже, предыдущий разработчик также внутренне обращался к элементам из вещей с помощью метода GetThing , что просто глупо (на мой взгляд ). Зачем вводить бессмысленные накладные расходы на дополнительные вызовы методов с использованием открытого интерфейса класса из самого класса? Если вы в классе, вы уже внутри реализации и можете получить доступ к частным / защищенным данным сколько угодно - не нужно притворяться иначе.
6
ответ дан 9 December 2019 в 22:28
поделиться

Если бы мне пришлось угадывать, я бы сказал, что этот разработчик использовал какой-то другой язык, например 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
1
ответ дан 9 December 2019 в 22:28
поделиться

Как указано в других ответах, это проблема ограничения доступа к самому массиву (или списку).

В общем, вы не хотите, чтобы клиенты этого класса могли напрямую изменять сам массив. Скрытие реализации базового списка также позволяет вам изменить реализацию в будущем, не затрагивая какой-либо клиентский код, который использует класс.

0
ответ дан 9 December 2019 в 22:28
поделиться

Здесь нужно отметить две вещи. Во-первых, вы хотите сохранить переменные объекта частными и использовать геттеры и сеттеры для доступа к ним. Это предотвращает случайное изменение или модификацию объектных переменных пользователем.

Во-вторых, считается хорошим соглашением об именовании, когда термины get/set должны использоваться при прямом доступе к атрибуту. Это помогает улучшить читабельность.

Хотя в данном случае это публичное свойство, использование геттеров и сеттеров улучшает читаемость и помогает предотвратить неожиданное поведение. Неважно, обращаетесь ли вы к переменным объекта в цикле, вы должны продолжать использовать соглашение GetThing.

Наконец, если вы обращаетесь к переменным изнутри объекта, вам не нужно использовать getter или setter.

ПРИМЕЧАНИЕ: Обычно считается хорошим стилем держать переменные объекта приватными и использовать геттеры/сеттеры для всех языков

Рекомендации по стилю C++

Рекомендации по стилю C#

Вас также может заинтересовать вопрос "getter и setter для класса в классе c#"

2
ответ дан 9 December 2019 в 22:28
поделиться

Возможно, вы захотите представить объект как 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");
2
ответ дан 9 December 2019 в 22:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: