Оператор сумісного використання властивостей для C #


9

Оператор нульового поєднання в c # дозволяє скоротити код

  if (_mywidget == null)
     return new Widget();
  else
     return _mywidget;

Аж до:

  return _mywidget ?? new Widget();

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

  if (_mywidget == null)
     return 5;
  else
     return _mywidget.Length;

З:

  return _mywidget.Length ??! 5;

Я не можу не думати, що для цього оператора повинні існувати певні причини. Це кодовий запах? Чи є якийсь кращий спосіб написати це? (Мені відомо про нульовий шаблон об'єкта, але здається надмірним його використання для заміни цих чотирьох рядків коду.)


1
Чи вистачить тут умовного оператора?
Анон.

1
Хтось написав щось, що дозволяє вам зробити щось подібне: рядок розташування = службовець.office.address.location ?? "Невідомий"; . Це встановить місце розташування на "Невідомо", коли один з об'єктів (або співробітник , офіс , адреса чи місцезнаходження ) недійсний. На жаль, я не пам'ятаю, хто це написав чи де він розмістив. Якщо я знову знайду його, опублікую тут!
Крістоф Клайс

1
Це питання отримає набагато більше тяги на StackOverflow.
робота

2
??!є оператором на C ++. :-)
Джеймс Мак-Нілліс

3
Привіт 2011, щойно закликав сказати, що c # отримує безпечний навігаційний оператор
Натан Купер,

Відповіді:


5

Я дуже хочу функцію мови C #, яка дозволяє мені безпечно робити x.Prop.SomeOtherProp.ThirdProp, не перевіряючи кожен з них на null. В одній базі коду, де мені довелося багато робити подібні "глибокі обходи властивостей", я написав метод розширення під назвою Navigate, який проковтнув NullReferenceExceptions і замість цього повернув значення за замовчуванням. Тож я міг би зробити щось подібне:

var propVal = myThing.Navigate(x => x.PropOne.PropTwo.PropThree.PropFour, defaultValue);

Недоліком цього є те, що він має інший кодовий запах: ковтають винятки. Якщо ви хотіли зробити щось подібне "правильно", ви можете прийняти lamdba як вираз і змінити вираз на ходу, щоб додати нульові перевірки навколо кожного доступу до властивості. Я пішов на "швидку і брудну" і не реалізував це таким "кращим" способом. Але, можливо, коли у мене будуть кілька запасних циклів думок, я перегляну це і десь опублікую.

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


1
Точно те, що я думав, але ефективність безпечного способу була б дуже поганою, я думаю (через багато Expression.Compile ()) ... Шкода, що це не реалізовано в C # (щось на зразок Obj.?PropA.?Prop1)
Guillaume86

x.PropOne.PropTwo.PropThree.PropFour - це поганий вибір дизайну, оскільки він порушує закон Деметера. Переробіть свої методи / класи, щоб не потрібно було цим користуватися.
Джастін Шилд

Бездумно уникати глибокого доступу до власності у світлі Закону Деметра так само небезпечно, як і бездумно нормалізувати базу даних. Можна зайти занадто далеко.
Мир,

3
У C # 6.0 це можливо за допомогою Null-Conditional Operator (він же оператор Elvis) msdn.microsoft.com/en-us/magazine/dn802602.aspx
victorvartan

15

Я вірю, що ви зможете це зробити із C # 6 досить просто зараз:

return _mywidget?.Length ?? 5;

Зверніть увагу на нульовий умовний оператор ?.з лівого боку. _mywidget?.Lengthповертає null, якщо _mywidgetnull.

Простий потрійний оператор може бути простішим для читання, хоча, як пропонують інші.


9

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

У будь-якому разі, я просто використовую умовний оператор і називаю його за день:

return (_mywidget != null) ? _mywidget.Length : 5;

?? - це лише ярлик до умовного оператора.


5
Особисто це настільки ж погано (імхо), як і оператор, що робить це в першу чергу. Ви хочете щось від об'єкта ... цього об’єкта може не бути ... тому давайте дамо 5відповідь у випадку, якщо його немає. Потрібно дивитися на свою конструкцію, перш ніж починати запитувати спеціальних операторів.
Му-сок

1
@ Moo-Juice Я просто уявляю, як людина підтримує цей код: "Чому це мені дає 5? Хрммм. Що?!'
msarchet


3

Особисто я вважаю, що це до читабельності. У першому прикладі ??перекладається досить гарно як "Ти там ?? Ні, давайте це зробимо".

У другому прикладі ??!явно WTF і нічого програмісту не означає .... об'єкта не існує, тому я не можу потрапити у властивість, тому я повернусь 5. Що це навіть означає? Що таке 5? Як ми дійшли висновку, що 5 - це хороша цінність?

Коротше кажучи, перший приклад має сенс ... не там, новий . По-друге, вона відкрита для дебатів.


2
Ну, ви повинні ігнорувати той факт, що ??! не існує. Уявіть, якщо ??! було звичайним явищем, і якщо воно використовувалося, то приймайте рішення на основі цього.
whatsisname

3
Це нагадує мені так званого "оператора WTF" в C ++ (це насправді працює:. (foo() != ERROR)??!??! cerr << "Error occurred" << endl;)
Tamás Szelei

@whatsisname, це було більше роздумом про те, що воно підходить для прийнятого рішення. Створювати об’єкт тому, що його там не було, має сенс. Присвоїти арбітуальне значення, яке нічого не означає для читача, оскільки ви не можете отримати властивість чогось, - це справді сценарій WTF.
Му-сок

Я думаю, ти потрапив у мій приклад. Можливо, 0 краще, ніж 5. Можливо, властивість є об'єктом, тому якщо _mywidget є нульовим, ви хочете повернути новий foodle (). Я повністю не згоден, що ?? читабельніше, ніж ??! хоч :)
Бен Фултон

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

2

Я думаю, що люди занадто зациклюються на "5", що ти повертаєшся ... можливо, 0 було б краще :)

У всякому разі, я думаю, що проблема полягає в ??!тому, що насправді це не окремий оператор. Поміркуйте, що це означатиме:

var s = myString ??! "";

У цьому випадку це не має сенсу: це має сенс лише в тому випадку, якщо лівий операнд є аксесуаром власності. А як щодо цього:

var s = Foo(myWidget.Length) ??! 0;

Там є доступ до власності, але я все ще не думаю, що це має сенс (якщо myWidgetце nullозначає, що ми взагалі не дзвонимо Foo()?) Чи це просто помилка?

Я думаю, що проблема полягає в тому, що вона просто не відповідає так природно мові ??.


1

Хтось створив клас корисності, який зробив би це для вас. Але я не можу його знайти. Я знайшов щось подібне на форумах MSDN (дивіться другу відповідь).

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


1

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

Наприклад, у вас об’єкти, прив’язані до карти, і ви хочете, щоб вони знаходилися на належному висоті. Карти DTED можуть мати отвори в своїх даних, тому можливо мати нульове значення, а також значення в діапазоні чогось типу -100 до ~ 8900 метрів. Можливо, ви хочете щось зробити так:

mapObject.Altitude = mapObject.Coordinates.DtedAltitude ?? DEFAULT_ALTITUDE;

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

Я вважаю це дуже цінним, але мої міркування, чому це не було зроблено, обмежуються складністю компілятора. Можуть бути випадки, коли поведінка не була б такою передбачуваною.


1

Коли я не маю справу з глибоко вкладеними, я використовував це (перед тим, як вводити нульовий оператор злиття в C # 6). Для мене це досить зрозуміло, але, можливо, це тому, що я звик до цього, інші люди можуть вважати це заплутаним.

return ( _mywidget ?? new MyWidget() {length = defaultLength}).Length;

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

Інша альтернатива - використання шаблону NullObject . Ви визначаєте один статичний екземпляр класу зі значущими значеннями за замовчуванням і використовуєте його.

return ( _mywidget ?? myWidget.NullInstance).Length;

0

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

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

Щось на кшталт: return (myobj ?? default_obj) .Length


0

Властивість length зазвичай є типом значення, тому воно не може бути нульовим, тому оператор узгодження нуля тут не має сенсу.

Але це можна зробити, наприклад, із властивістю, що є еталонним типом: var window = App.Current.MainWindow ?? new Window();


На прикладі намагається врахувати, що трапилося б у вашій ситуації, якби Current був недійсним. Ваш приклад би просто вийшов з ладу ... але було б зручно мати оператора, який може звести нуль в будь-яку точку ланцюга перенаправлення.
Мир,

-1

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

return _obj ?? (_obj = new Class());

тому ідея полягала в тому, щоб об'єднати ??та =призначити:

return _obj ??= new Class();

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

Ця ідея не моя, але мені дуже подобається :)


1
Ви жертва бідного прикладу? Ваш приклад можна скоротити, щоб return _obj ?? new Class();тут не було потреби ??=.
Метт Еллен

@Matt Ellen ти неправильно зрозумів Завдання призначене . Я пропоную іншу альтернативу. Це не зовсім так, як просив ОП, але я вважаю, що це буде так само корисно.
чакрит

2
чому ви хочете призначити, якщо ви збираєтесь повернути значення?
Метт Еллен

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