Перемикач або словник при призначенні нового об'єкта


12

Останнім часом я віддаю перевагу відображення взаємозв'язків 1-1 із використанням Dictionariesзамість Switchвисловлювань. Я вважаю, що це трохи швидше писати і легше розумово обробляти. На жаль, під час відображення нового екземпляра об'єкта, я не хочу його визначати так:

var fooDict = new Dictionary<int, IBigObject>()
{
    { 0, new Foo() }, // Creates an instance of Foo
    { 1, new Bar() }, // Creates an instance of Bar
    { 2, new Baz() }  // Creates an instance of Baz
}

var quux = fooDict[0]; // quux references Foo

Враховуючи цю конструкцію, я витратив циклічні процесори та створив пам'ять, створивши 3 об'єкти, роблячи все, що можуть містити їх конструктори, і лише в кінцевому підсумку використовую один з них. Я також вважаю, що відображення інших об'єктів fooDict[0]у цьому випадку спричинить посилання на те саме, а не створення нового екземпляра за Fooпризначенням. Рішенням буде замість цього використовувати лямбда:

var fooDict = new Dictionary<int, Func<IBigObject>>()
{
    { 0, () => new Foo() }, // Returns a new instance of Foo when invoked
    { 1, () => new Bar() }, // Ditto Bar
    { 2, () => new Baz() }  // Ditto Baz
}

var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'

Це доходить до точки, коли це занадто заплутано? Це легко пропустити ()в кінці. Або відображення функції / вираження є досить поширеною практикою? Альтернативою було б використання перемикача:

IBigObject quux;
switch(someInt)
{
    case 0: quux = new Foo(); break;
    case 1: quux = new Bar(); break;
    case 2: quux = new Baz(); break;
}

Яке виклик є більш прийнятним?

  • Словник для швидшого пошуку та зменшення кількості ключових слів (регістр та перерва)
  • Перемикач: Частіше зустрічається в коді, не вимагає використання об'єкта Func <> для непрямості.

2
без лямбда ви будете повертати один і той же екземпляр кожного разу, коли ви здійснюєте пошук тим самим ключем (як і в fooDict[0] is fooDict[0]). і з лямбда, і з вимикачем це не так
храповик вирод

@ratchetfreak Так, я насправді зрозумів це, коли набирав приклад. Думаю, я десь занотував це.
KChaloux

1
Я здогадуюсь, той факт, що ви чітко ставите це до константи, означає, що вам потрібно створити об'єкт, щоб його можна змінити. Але якщо одного дня ви зможете зробити їх непорушними, то повернення об’єкта безпосередньо стане найкращим рішенням. Ви можете поставити дикт у поле const і понести витрати на створення лише один раз у всій програмі.
Лоран Бургу-Рой

Відповіді:


7

Це цікаво взяти на заводський зразок . Мені подобається поєднання словника та лямбдаського виразу; це змусило мене подивитися на цей контейнер по-новому.

Я ігнорую стурбованість у вашому запитанні щодо циклів процесора, як ви згадували в коментарях, що не лямбда-підхід не забезпечує те, що вам потрібно.

Я думаю, що будь-який підхід (перехід проти словника + лямбда) буде добре. Єдине обмеження полягає в тому, що за допомогою словника ви обмежуєте типи входів, які ви могли б отримати, щоб генерувати клас, що повертається.

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

Якщо це нове для вашої команди, прокоментуйте код та поясніть, що відбувається. Зателефонуйте на перегляд коду команди, ознайомте їх із зробленим та дайте їм знати про це. Крім цього, це виглядає чудово.


На жаль, як і близько місяця тому, моя команда складається виключно із мене (ведуча кинула). Я не думав про його відповідність фабриці. Це насправді акуратне спостереження.
KChaloux

1
@KChaloux: Звичайно, якщо ви використовували лише шаблон «Фабричного методу», ваш текст case 0: quux = new Foo(); break;стає case 0: return new Foo();відверто так само простим для написання та набагато простішим для читання, ніж{ 0, () => new Foo() }
пдр

@pdr Це вже показано декілька місць у коді. Напевно, є вагомий привід створити заводський метод на об’єкті, який надихнув це питання, але я подумав, що це досить цікаво, щоб задати його самостійно.
KChaloux

1
@KChaloux: Зізнаюся, я не зацікавлений у недавній одержимості заміною перемикача / корпусу словником. Я ще не бачив жодного випадку, коли спрощення та ізоляція комутатора власним методом не було б ефективнішим.
пдр

@pdr Нав'язливість - це сильне слово для вживання тут. Більше уваги при вирішенні питання про порядок одноразового відображення між значеннями. Я погоджуюся, що у випадках, коли це повторюється, найкраще виділити метод творчості.
KChaloux

7

C # 4.0 дає вам Lazy<T>клас, який схожий на ваше власне друге рішення, але виразніше викрикує "Ледачу ініціалізацію".

var fooDict = new Dictionary<int, Lazy<IBigObject>>()
{
    { 0, new Lazy(() => new Foo()) }, // Returns a new instance of Foo when invoked
    { 1, new Lazy(() => new Bar()) }, // Ditto Bar
    { 2, new Lazy(() => new Baz()) }  // Ditto Baz
}

О, я цього не знав.
KChaloux

О, це добре!
Лоран Буро-Рой

2
Однак, як тільки викликається Lazy.Value, він використовує той самий екземпляр протягом свого життя. Дивіться Ледачу ініціалізацію
Ден Ліон

Звичайно, інакше це не буде лінивою ініціалізацією, просто повторною ініціалізацією кожного разу.
Авнер Шахар-Каштан

ОП заявляє, що йому це потрібно для створення нових примірників кожного разу. Друге рішення з лямбдами і третє рішення з комутатором обидва роблять це, тоді як перше рішення та реалізація Lazy <T> цього не роблять.
Дан Ліонс

2

Стилістично я вважаю, що читабельність між ними рівна. Простіше зробити ін'єкцію залежності з Dictionary.

Не забувайте, що ви повинні перевірити, чи існує ключ під час використання Dictionary, і потрібно забезпечити резервний запас, якщо його немає.

Я вважаю за краще switchвисловлювання для статичних кодових шляхів та Dictionaryдля динамічних кодових шляхів (де ви можете додавати або видаляти записи). Компілятор, можливо, зможе виконати деякі статичні оптимізації з тим, switchщо він не може з Dictionary.

Цікаво, що ця Dictionaryзакономірність - це те, що люди іноді роблять у Python, оскільки Python не вистачає цього switchтвердження. В іншому випадку вони використовують ланцюги if-else.


1

Взагалі я б не віддав перевагу жодному.

Що б не споживало, це повинно працювати з a Func<int, IBigObject>. Тоді джерелом вашого відображення може бути Словник або метод, який містить оператор перемикання або виклик веб-служби або якийсь пошук файлів ... як завгодно.

Щодо реалізації, то я вважаю за краще Словник, оскільки його легше переробити з «словника жорсткого коду, ключа пошуку, результату повернення» до «словника завантаження з файлу, ключа пошуку, результату повернення»

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.