среда, 2 февраля 2011 г.

ToString Or Empty If Null. Строковое значение объекта. Маленькие хитрости

Недавно мне понадобилось получать строковые значения некоторых объектов, т.е. нужно было получать результат вызова метода ToString(). Если объект нулевой (null) нужно было получать пустую строку. Задача осложнялась тем, что таких объектов было много. А в некоторых случаях нужно было получить строковое значение членов объектов (свойств), которые, в свою очередь, также могли быть нулевыми.

Но буду последователен и посмотрим, как бы решалась задача для простого типа, например String:
String obj = "value";
// ..
String str = (obj == null) ? String.Empty : obj.ToString();


* This source code was highlighted with Source Code Highlighter.

Как уже было сказано, проверок на null оказалось слишком много, так что код начал терять читабельность. Пришлось вводить метод-расширение. За счет этого удалось сократить код и повысить его восприятие:
String obj = "value";
// ..
String str = obj.xStringOrEmptyIfNull();


* This source code was highlighted with Source Code Highlighter.

Метод-расширение выглядит так:
public static class ExtensionsString
{
  public static String xStringOrEmptyIfNull(this Object obj) {
    return (obj != null) ? obj.ToString() : String.Empty;
  }
}


* This source code was highlighted with Source Code Highlighter.

Все очень просто, но не забываем про вторую часть задачи, где нужно получать строковое значение свойства объекта. Вот классы для примера:
public class Person
{
  public String Name { get; set; }
  public Language Language { get; set; }
}

public class Language
{
  public String LangName { get; set; }
  public int Level { get; set; }
}


* This source code was highlighted with Source Code Highlighter.

Имея объект класса Person нужно определить название языка, либо получить пустую строку, если свойство язык равно нулю. Решение в лоб:
String lang = (p.Language != null) ? p.Language.LangName.xStringOrEmptyIfNull() : String.Empty;

* This source code was highlighted with Source Code Highlighter.

Выглядит не очень, и это несмотря на использование метода-расширения, существенно сокращающего код. Так вот, мне удалось сократить код до слудующей конструкции:
String lang = p.Language.xStringOrEmptyIfNull(x => x.LangName);

* This source code was highlighted with Source Code Highlighter.

Уже намного лучше. Вот так выглядит метод-расширение:
public static String xStringOrEmptyIfNull<T>(this T t, Func<T, Object> del) {
  return (t == null) ? String.Empty : del(t).xStringOrEmptyIfNull();
}


* This source code was highlighted with Source Code Highlighter.

Весь секрет в делегатае Func<..>. Кто еще не очень с ним знаком, советую на него более пристально посмотреть. Вся магия LINQ построена на нем и я сам иногда прибегаю к его использованию, например, так как это сделано в примере выше.