Чому є метод, який повертає bool / int і має фактичний об'єкт як вихідний параметр?


12

У кодовій базі моєї компанії я бачу таку схему коду (додаток .NET 3.5):

bool Foo(int barID, out Baz bazObject) { 
    try { 
            // do stuff
            bazObject = someResponseObject;

            return true;
    }
    catch (Exception ex) { 
        // log error
        return false;
    }
}

// calling code
BazObject baz = new BazObject();
fooObject.Foo(barID, out baz);

if (baz != null) { 
    // do stuff with baz
}

Я намагаюся обернути голову навколо того, чому б ви зробили це замість того, щоб Fooметод просто взяв ідентифікатор і повернув Bazоб'єкт, замість того, щоб повертати значення, яке не використовується, і фактичний об'єкт бути параметром ref або output.

Чи є якась прихована користь цього стилю кодування, яка мені не вистачає?



У вашому прикладі bazбуття nullі повернене boolбуття неfalse є рівнозначними. ніколи не є , тому, якщо не буде оновлено до того, як введено , коли його повернути, він ніколи не буде . Це допомогло б надзвичайно , якщо специфікації для були доступні. Насправді це, мабуть, найсерйозніша проблема, викрита цим кодом. new BazObject()nullbazObjectExceptionFoofalsebaznullFoo
Стів Пауелл

Моя пам'ять туманне, як це було давно, але я думаю, що я зіпсував цей приклад, і він перевіряв, чи "baz" неправдивий, а не, якщо він був нульовим. У будь-якому випадку модель просто здалася мені справді архаїчною, як і від VB6, і розробник ніколи не намагався вдосконалити свій код (чого він не зробив)
Wayne Molina

Відповіді:


11

Зазвичай ви використовуєте цей шаблон, щоб ви могли написати такий код:

if (Foo(barId, out bazObject))
{
  //DoStuff with bazobject
}

наприклад, використовується в CLR для TryGetValue в класі словника. Це дозволяє уникнути надмірності, але параметри та параметри для мене завжди здавалися делікатними


1
+1, це причина, чому, хоч я схильний до повернення об'єкта як з булевим, так і з об'єктом, а не повертати їх обома окремо
pdr

Визначте це як відповідь, оскільки це пояснює причину, хоча консенсус, здається, є досить застарілим, за винятком конкретних обставин.
Уейн Моліна

Ця відповідь виправдовує використання цього шаблону. Але якщо ВСЕ ЗА ТВОЮ базу коду, це, мабуть, погана практика на зразок Шона.
Кодизм

11

Це старий код стилю C, раніше були винятки. Повернене значення вказувало, чи був метод успішним чи ні, і якщо він був, параметр буде заповнений результатом.

У .net у нас є винятки для цієї мети. Не повинно бути причин слідувати цій схемі.

[редагувати] Очевидно, що в роботі з виключеннями є ефективність. Можливо, це має щось спільне з цим. Однак у цьому фрагменті коду вже є виняток. Було б чистіше просто дозволити йому переміщатися вгору по стеку, поки він не потрапить у більш відповідне місце.


Ні. Не можу з цим погодитися. Ніколи не використовуйте виняток для очікуваного стану. Наприклад, якщо ви аналізуєте рядок до int, завжди можливо, хтось передасть непридатний рядок. У цьому випадку ви не повинні кидати виняток
pdr

Це не те, що я сказав. Якщо ви читаєте код, який опублікував ОП, явно відбувається виняток, але метод перехоплює його і натомість повертає помилку. Це питання пов'язане з обробкою помилок, не очікуваними умовами.
Шон Едвардс

Так, це, здається, в основному використовується в методах, які завантажують дані з бази даних, наприклад, if (baz.Select())але частіше за все повернене значення просто викидається, а значення перевіряється на нульове чи якесь властивість.
Уейн Моліна

@Sean, подивись, що ти говориш, я просто заперечую проти фрази "У .NET. У нас для цього є винятки". Винятки служать для мене зовсім іншою метою
пдр

1
Гаразд, я зрозумію. Це правда, що ви не повинні просто додавати булевий метод до кожного методу для обліку помилок кодування недійсних аргументів. Але там, де вхід до методу надходить не від розробника, а від розробника, ви повинні намагатися обробляти дані, не кидаючи виняток, якщо вони неправильно зрозуміли.
пдр

2

З огляду на фрагмент коду, він виглядає абсолютно безглуздо. Для мене початкова схема коду підказує, що null для BazObject буде прийнятним випадком, а повернення bool є мірою безпечного визначення випадку відмови. Якщо такий код був:

// calling code
BazObject baz = new BazObject();
bool result = fooObject.Foo(barID, out baz);

if (result) { 
    // do stuff with baz
    // where baz may be 
    // null without a 
    // thrown exception
}

Для цього було б більше сенсу робити це так. Можливо, це метод, який хтось перед вами використовував, щоб гарантувати, що база передається посиланням, не розуміючи, як параметри об’єкта насправді працюють у C #.


Я думаю, що це написав нинішній член команди. Можливо, це те, про що я повинен хвилюватись O_O
Wayne Molina

@Wayne M: Менше турбот, ніж потенційна можливість тренувань :)
Джоель Етертон

1

Іноді ця схема корисна, коли ви, абонент, не повинні перейматися, чи повертається тип є посилальним чи значенням. Якщо ви викликаєте такий метод для отримання типу значень, для цього типу значень знадобиться встановлене недійсне значення (наприклад, double.NaN), або вам потрібен інший спосіб визначення успіху.


Що має сенс. Здається трохи дивно, але це звучить як поважна причина.
Уейн Моліна

0

Ідея полягає у поверненні значення, яке вказує на те, чи був процес успішним, дозволяючи такий код:

Baz b;
if (fooObject.foo(id, out b)) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Якщо об’єкт не може бути нульовим (що здається правильним у вашому прикладі), краще зробити наступним чином:

Baz b = fooObject.foo(id);
if (b != null) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Якщо об’єкт може бути недійсним, винятки - це спосіб перейти:

try {
   Baz b = fooObject.foo(id);
}
catch (BazException e) {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Це звичайний зразок, який використовується в C, де немає винятків. Це дозволяє функції повернути стан помилки. Винятки, як правило, набагато чистіші рішення.


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