Як зробити ToString для можливо нульового об'єкта?


97

Чи існує простий спосіб зробити наступне:

String s = myObj == null ? "" : myObj.ToString();

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

String s = "" + myObj;

Було б чудово, якби Convert.ToString () мав для цього належне перевантаження.


3
Я не бачу нічого поганого в першій. Якщо ви вважаєте, що другий - хакерство, найкраще просто написати функцію утиліти, яка виконує нульову перевірку.
— Нік Ларсен

plz, ви можете бути більш точним у своєму питанні
— FosterZ

3
string.Format ("{0}", myObj) приймає нульові значення.
— Хольстебро,

3
З C # 6.0 ми тепер можемо використовувати нульові умовні оператори, такі як TheText? .ToString () або TheText? .Trim ()
— Santosh,

2
Відповідно до цієї відповіді Convert.ToString() робиться саме те перше, що ви написали нижче.
— drzaus

Відповіді:


175

C # 6.0 Редагувати:

З C # 6.0 ми тепер можемо мати стислу, оригінальну версію методу orignal:

string s = myObj?.ToString() ?? "";

Або навіть за допомогою інтерполяції:

string s = $"{myObj}";

Оригінальна відповідь:

String s = (myObj ?? String.Empty).ToString();

або

String s = (myObjc ?? "").ToString()

щоб бути ще більш лаконічним.

На жаль, як вже зазначалося, вам часто знадобиться привід з будь-якої сторони, щоб зробити цю роботу з типами не String або Object:

String s = (myObjc ?? (Object)"").ToString()
String s = ((Object)myObjc ?? "").ToString()

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

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

public static string ToStringNullSafe(this object value)
{
    return (value ?? string.Empty).ToString();
}

Це буде компіляція? Хіба оператор злиття не перевіряє типи?
— Нік Ларсен,

1
Він працює, оскільки (object ?? string) повертає об'єкт, тому що для об'єднання використовується найменш поширений тип. Але я думаю, що це не працює для інтерфейсів, оскільки він не може вирішити, який інтерфейс вибрати (оскільки для кожного класу дозволено декілька інтерфейсів).
— codymanix

1
Не зовсім рішення, на яке я сподівався, але я його прийму
— codymanix

4
Проголосувати за цей акторський склад страшно потворно і передбачає бокс.
— steinar

1
перший варіант c # 6 дуже елегантний
— Alexandre N.

40
string.Format("{0}", myObj);

string.Format відформатує null як порожній рядок і викличе ToString () для ненульових об'єктів. Як я розумію, це те, що ви шукали.


3
Мені особисто це не подобається. Код може бути незначно коротшим за String s = myObj == null? "": myObj.ToString (), але ви повністю приховуєте аргументи свого методу.
— AGuyCalledGerald

Звичайно, це мікрооптимізація, але вона займає приблизно в 5 разів більше, ніж просто "" + myObj. Але я вже читав, що створює зайві рядки. str.Concat(myObj)здається, працює нормально і "ще швидше".
— drzaus

18

Було б чудово, якби Convert.ToString () мав для цього належне перевантаження.

Існує Convert.ToString(Object value)версія .Net 2.0 (приблизно 5 років до того, як запитували цей Q), яка, схоже, робить саме те, що ви хочете:

http://msdn.microsoft.com/en-us/library/astxcyeh(v=vs.80).aspx

Я тут чогось дійсно очевидного втрачаю / неправильно трактую?


1
Ти ні, вони роблять. Чому просто, коли це може бути складно :)
— alex.peter

13

За допомогою методу розширення ви можете досягти цього:

public static class Extension
{
    public static string ToStringOrEmpty(this Object value)
    {
        return value == null ? "" : value.ToString();
    }
}

Наступне нічого не писатиме на екран і не створюватиме винятку:

        string value = null;

        Console.WriteLine(value.ToStringOrEmpty());

Чи опускають виклики методу розширення звичайну перевірку на нульові значення ...?
— Матті Вірккунен,

2
Це додає, що вам не потрібно вводити "{0}", і ви можете вибрати назву методу, яка описує те, що ви робите. Це особливо корисно, коли ви робите це багато разів. Ви навіть можете назвати метод ToStringOrEmptyWhenNull.
— Пітер ван Гінкель,

2
Якщо ОП вважає string s = "" + myObj;хакерською, виклик функції на нульовому об'єкті повинен потрапити в ту саму лодку. Я б підтримав це, але це вирішує проблему, я просто не погоджуюсь із використанням. Нульові об'єкти повинні кидати NullReferenceException, навіть у методах розширення.
— Нік Ларсен

2
@NickLarsen: Я з більшістю випадків погоджуюся з тобою, але іноді ти просто хочеш зручності простого дзвінка методом. Назва методу повинна дати вам натяк на те, що нульові посилання в порядку, як і в пропозиції для ToStringOrEmptyWhenNull.
— Jordão

3
Методи розширення не викидають, коли параметр "перший" ( thisпараметр), оскільки вони є просто синтаксичним цукром. Тобто x.SomeExtensionMethod()це синтаксичний SomeStaticClass.SomeExtensionMethod(x);Таким чином, коли xце nullми не намагаємося викликати метод на nullоб'єкті, а передаючи nullоб'єкт на статичний метод. Тоді і лише тоді, коли цей метод перевіряє наявність nullпараметра і кидає, коли зустрічає такий, метод розширення буде "викликаний" при nullметанні об'єкта.
— jason

7

Я не згоден з цим:

String s = myObj == null ? "" : myObj.ToString();

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

ОНОВЛЕННЯ:

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


Він не сказав, що цей метод був зламаним. Насправді він не сказав, що з цим не так, крім, можливо, натякаючи, що це не просто. Я не думаю, що в цьому методі є щось погане. +1, бо я б зробив це таким чином.
— Mark Byers

Я погоджуюсь з OP ... у c # вже є нульовий оператор злиття - див. Мій пост нижче.
— Ендрю Хенлон,

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

@Pieter Ярмарок. Але це так. Питання: "Чи є простий спосіб зробити наступне: ...". Точно такий спосіб простий!
— steinar

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

4
string s = String.Concat(myObj);

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


2
Це явна форма "" + myObj, але ще гірше, коли ви розповідаєте, що тут відбувається. Цікаво в будь-якому випадку.
— codymanix

@codymanix, я не думаю, що це те саме - Concatнасправді робить нульову перевірку внизу і повертає string.Emptyабо arg0.ToString(), що, здається, трохи ефективніше (я маю на увазі, ми тут говоримо про ms).
— drzaus

2

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

string s = string.Empty;
    if(!string.IsNullOrEmpty(myObj))
    {
    s = myObj.ToString();
    }

Звичайно, я можу написати це так, але я хочу простий невеликий виклик функції, мені було цікаво, чому в .NET BCL немає нічого подібного
— codymanix

Вам справді потрібна функція, поки ми маємо метод IsNullOrEmpty ()?
— Серкан Хекімоглу

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

1
string s = string.IsNullOrEmpty(myObj) ? string.Empty : myObj.ToString();
— Нік Ларсен,

2

Я можу бути побитий за свою відповідь, але тут все одно йдеться:

Я б просто писав

рядок s = "" if (myObj! = null) {x = myObj.toString (); }

Чи є виграш з точки зору продуктивності за використання потрійного оператора? Я не знаю, що з голови.

І зрозуміло, як хтось вище згадував, ви можете застосувати цю поведінку до такого методу, як safeString (myObj), що дозволяє повторно використовувати.


OMG, якщо об’єкт нульовий, тоді на ньому викликати ToString ()?
— codymanix

Давай, це друкарська помилка. Я замінив перший = на! щоб це було правильно. Але ти справді збираєшся до мене -1 для друкарської помилки?
— jaydel

2

У мене була та ж проблема, і я вирішив її, просто перекинувши об’єкт у рядок. Це працює і для нульових об'єктів, оскільки рядки можуть бути нульовими. Якщо ви абсолютно не хочете мати нульовий рядок, це має спрацювати чудово:

string myStr = (string)myObj; // string in a object disguise or a null

Найкоротше і найкраще рішення. (Obj1 ?? "") .ToString () фактично спочатку також передає Obj1 як рядок :)
— TPAKTOPA

2

Деякі (швидкісні) тести продуктивності, що підсумовують різні варіанти, не те, що це насправді має значення #microoptimization (з використанням розширення linqpad )

Параметри

void Main()
{
    object objValue = null;
    test(objValue);
    string strValue = null;
    test(strValue);
}

// Define other methods and classes here
void test(string value) {
    new Perf<string> {
        { "coallesce", n => (value ?? string.Empty).ToString() },
        { "nullcheck", n => value == null ? string.Empty : value.ToString() },
        { "str.Format", n => string.Format("{0}", value) },
        { "str.Concat", n => string.Concat(value) },
        { "string +", n => "" + value },
        { "Convert", n => Convert.ToString(value) },
    }.Vs();
}

void test(object value) {
    new Perf<string> {
        { "coallesce", n => (value ?? string.Empty).ToString() },
        { "nullcheck", n => value == null ? string.Empty : value.ToString() },
        { "str.Format", n => string.Format("{0}", value) },
        { "str.Concat", n => string.Concat(value) },
        { "string +", n => "" + value },
        { "Convert", n => Convert.ToString(value) },
    }.Vs();
}

Можливо, важливо зазначити, що Convert.ToString(...)буде збережено нульовий рядок.

Результати

Об'єкт

  • nullcheck 1,00x 1221 тиків минуло (0,1221 мс) [у 10K повторень, 1,221E-05 мс за]
  • вуглецевий 1.14x 1387 кліків минуло (0.1387 мс) [у 10K повторень, 1.387E-05 мс за]
  • рядок + 1,16x 1415 тиків минуло (0,1415 мс) [у 10K повторень, 1,415E-05 мс за]
  • str.Concat 1,16x 1420 кліків минуло (0,142 мс) [у 10K повторень, 1,42E-05 мс за]
  • Перетворення 1,58x 1931 тиків, що минули (0,1931 мс) [у 10 тис. Повторень, 1,931E-05 мс за]
  • str.Format 5.45x 6655 тиків минуло (0.6655 мс) [у 10K повторень, 6.655E-05 мс за]

Рядок

  • nullcheck 1,00x 1190 тиків минуло (0,119 мс) [у 10K повторень, 1,19E-05 мс за]
  • Перетворення 1.01x 1200 тиків, що минули (0,12 мс) [у 10K повторень, 1,2E-05 мс за]
  • рядок + 1,04x 1239 тиків минули (0,1239 мс) [у 10K повторень, 1,239E-05 мс за]
  • вуглес 1,20x 1423 тиків минуло (0,1423 мс) [у 10K повторень, 1,423E-05 мс за]
  • str.Concat 4,57x 5444 минулих кліків (0,5444 мс) [у 10K повторень, 5,444E-05 мс за]
  • str.Формат 5,67x 6750 минулих кліків (0,675 мс) [у 10K повторень, 6,75E-05 мс за]

1

Коментар Хольстебро буде вашою найкращою відповіддю:

string s = string.Format("{0}", myObj);

Якщо myObjмає значення null, формат розміщує там значення порожнього рядка.

Він також задовольняє ваші вимоги в один рядок і легко читається.


так, але код незрозумілий. Скільки ваших колег-розробників знатимуть, що ви намагаєтесь досягти?
— AGuyCalledGerald

0

Незважаючи на те, що це давнє запитання, і ОП просив C #, я хотів би поділитися рішенням VB.Net для тих, хто працює з VB.Net, а не C #:

Dim myObj As Object = Nothing
Dim s As String = If(myObj, "").ToString()

myObj = 42
s = If(myObj, "").ToString()

На жаль, VB.Net не дозволяє? -Operator після змінної, тому myObj? .ToString недійсний (принаймні не в .Net 4.5, який я використовував для тестування рішення). Натомість я використовую If, щоб повернути порожній рядок на випадок, якщо myObj ist Нічого. Отже, перший виклик Tostring повертає порожній рядок, тоді як другий (де myObj не є Ніщо) повертає "42".

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