Как сделать ToString для возможно нулевого объекта?


99

Есть ли простой способ сделать следующее:

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

Я знаю, что могу сделать следующее, но я действительно считаю это хакерством:

String s = "" + myObj;

Было бы здорово, если бы Convert.ToString () имел для этого подходящую перегрузку.


3
Я не вижу ничего плохого в первом. Если вы считаете, что второе - это взлом, лучше всего просто написать служебную функцию, которая выполняет нулевую проверку.
Ник Ларсен,

пожалуйста, не могли бы вы уточнить свой вопрос
FosterZ

3
string.Format ("{0}", myObj) принимает нулевые значения.
Holstebroe

3
В C # 6.0 теперь мы можем использовать условные операторы null, такие как theText? .ToString () или theText? .Trim ()
Сантош

2
За этот ответ Convert.ToString() делает то первое , что вы написали под ним.
drzaus 01

Ответы:


179

C # 6.0 Редактировать:

С C # 6.0 теперь у нас может быть сжатая версия исходного метода без приведения типов:

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) возвращает объект, потому что coalesce использует наименее распространенный тип. Но я думаю, что это не сработает для интерфейсов, потому что он не может решить, какой интерфейс выбрать (поскольку для каждого класса разрешено несколько интерфейсов).
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 01

18

Было бы здорово, если бы Convert.ToString () имел для этого подходящую перегрузку.

С тех пор существует Convert.ToString(Object value).Net 2.0 (примерно за 5 лет до того, как этот вопрос был задан), который, похоже, делает именно то, что вы хотите:

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
Если OP считает, что string s = "" + myObj;это хакерство, вызов функции для нулевого объекта должен попасть в ту же лодку. Я бы проголосовал против этого, но это решает проблему, я просто не согласен с использованием. Нулевые объекты должны бросать NullReferenceExceptionдаже в методах расширения.
Ник Ларсен,

2
@NickLarsen: Я согласен с вами в большинстве случаев, но иногда вам просто нужно удобство простого вызова метода. Имя метода должно указывать на то, что пустые ссылки допустимы, как и в случае с предложением для ToStringOrEmptyWhenNull.
Жордау,

3
Методы расширения не генерируют «первый» параметр ( thisпараметр), потому что они просто синтаксический сахар. То есть x.SomeExtensionMethod()это синтаксический SomeStaticClass.SomeExtensionMethod(x);Таким образом, когда xэто nullмы не пытаемся вызвать метод на nullобъекте, а передавая nullобъект на статический метод. Если и только если этот метод проверяет nullпараметр и выдает при обнаружении такого параметра метод расширения, «вызываемый» для nullобъекта throw.
Джейсон

7

Я не согласен с этим:

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

это хак в любом случае. Думаю, это хороший пример понятного кода. Абсолютно очевидно, чего вы хотите достичь и ожидаете null.

ОБНОВИТЬ:

Теперь я понимаю, что вы не говорили, что это была взлом. Но в вопросе подразумевается, что вы думаете, что этот путь не подходит. На мой взгляд, это определенно самое ясное решение.


Он не сказал, что этот метод был взломом. На самом деле он не сказал, что с этим не так, за исключением, возможно, намекнув, что это было непросто. Я не думаю, что с этим методом что-то не так. +1 потому что я бы так сделал.
Марк Байерс,

Я согласен с OP ... в С # уже есть нулевой оператор объединения - см. Мой пост ниже.
Эндрю Хэнлон,

Я думаю, что в этом случае оператор слияния с нулевым значением немного менее понятен, однако я бы с таким же успехом справился с этим.
Steinar

@ Питер Достаточно справедливо. Но это так. Возникает вопрос: «Есть ли простой способ сделать следующее: ...». Это очень просто!
Steinar

Я никогда не говорил, что это метод взлома. Пожалуйста, перечитайте мой вопрос. Мне не хватает только небольшой функции в Framework, которая имеет эту функцию.
codymanix

4
string s = String.Concat(myObj);

Я думаю, это был бы самый короткий путь, а также имел бы незначительные накладные расходы на производительность. Имейте в виду, что читателю кода будет не совсем понятно, в чем заключается намерение.


2
Это явная форма "" + myObj, но еще хуже для того, чтобы рассказать о том, что здесь происходит. Все равно интересно.
codymanix

@codymanix Я не думаю, что это то же самое - на Concatсамом деле внизу выполняется проверка на null и возвращает string.Emptyor arg0.ToString(), что кажется немного более производительным (я имею в виду, мы говорим здесь ms).
drzaus 01

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 = "" если (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 09

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,00 x 1221 тик прошло (0,1221 мс) [за 10 000 повторений, 1,221E-05 мс за]
  • Coallesce 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 000 повторений, 1,931E-05 мс за]
  • str.Format 5,45x Прошло 6655 тиков (0,6655 мс) [за 10 000 повторений, 6,655E-05 мс за]

Строка

  • nullcheck 1,00x 1190 тактов (0,119 мс) [в 10K повторений, 1,19E-05 мс за]
  • Преобразование 1,01 x 1200 прошедших тиков (0,12 мс) [за 10 000 повторений, 1,2E-05 мс за]
  • строка + 1,04x прошедшее 1239 тактов (0,1239 мс) [за 10 тыс. повторений, 1,239E-05 мс за]
  • Coallesce Прошло 1,20 x 1423 тика (0,1423 мс) [за 10 000 повторений, 1,423E-05 мс за]
  • str.Concat 4,57x Прошло 5444 тика (0,5444 мс) [в 10K повторений, 5,444E-05 мс за]
  • str.Format 5,67x Прошло 6750 тиков (0,675 мс) [за 10 000 повторений, 6,75E-05 мс за]

1

Комментарий Холстебро был бы вашим лучшим ответом:

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

Если myObjимеет значение null, Format помещает туда значение пустой строки.

Он также удовлетворяет вашим требованиям к одной строке и легко читается.


да, но код непонятен. Сколько из ваших коллег-разработчиков будут знать, чего вы пытаетесь достичь?
AGuyCalledGerald

0

Несмотря на то, что это старый вопрос, и OP запросил 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 не позволяет использовать? -Оператор после переменной, поэтому myObj? .ToString недействителен (по крайней мере, не в .Net 4.5, который я использовал для тестирования решения). Вместо этого я использую If для возврата пустой строки в случае, если myObj ist Nothing. Таким образом, первый Tostring-Call возвращает пустую строку, а второй (где myObj не Nothing) возвращает «42».

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.