Як зробити 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.