Введение в проблему


Казалось бы, такая простая тема, как преобразование типов значений (value conversion), не достойна целой статьи. В C# есть подходящий оператор "(T)value", есть типы, которые его реализуют, и тему на этом можно считать закрытой. Но за 14 лет существования .NET разработчики BCL и другие программисты придумали еще 4 способа преобразовывать типы значений.



Вот их список:

  1. System.Convert class;
  2. IConvertible interface;
  3. System.ComponentModel.TypeConverter;
  4. To, From, Parse, Create methods.

В добавок к этому существуют мета-типы Nullable[T], System.Enum, у которых свои методы преобразования типов значений.

И у всего этого разнообразия методов не было одного удобного API, чтобы преобразовать A>B, не думая о том, каким способом разработчик реализовал возможность этого преобразования.

Решение


Класс TypeConvert в пространстве имен System (да, я знаю, что засорять чужие неймспейсы плохо).

ToType Convert<FromType, ToType>(FromType value, string format, IFormatProvider formatProvider)

Преобразует тип значения из FromType в ToType, опционально используя format и настройки форматирования formatProvider. Если подходящих методов, принимающих настройки форматирования, нет, то будет вызван вариант без учета форматирования.

В README файле есть описание других методов.

Установка


Всё это лежит в одном файле, который можно забрать к себе в проект и в удобном Nuget-пакете:

Install-Package TypeConvert 

В нагрузку к Nuget-пакету идут 2 класса:

TypeActivator — создание экземпляров типа через закешированный конструктор,
HexConvert — преобразование байтов/чисел в hexadecimal представление и обратно.

Применение


В проекте ConsoleApp.CommandLine для биндинга передаваемых параметров неизвестного типа к известным параметрам метода.

В приватных проектах для преобразования значений конфигурации полученной из JSON к конкретным сложным типам (Uri, IpAddress, TimeSpan...).

Ссылки


» Проект на гитхабе
Поделиться с друзьями
-->

Комментарии (9)


  1. ARad
    16.11.2016 12:39

    Все это замечательно, но как раз такой универсальный метод, который может выполнится разными способами (при обновлениях библиотек) может как раз подставить программиста и породит неожиданные ошибки.


    1. shai_hulud
      16.11.2016 12:56

      Да, за «магию» надо платить. Как из за любой другой долг знаний.
      От этого поможет только регрессионное тестирование.


  1. aleksei_korobov
    16.11.2016 12:41

    Спасибо! возьму на заметку.


  1. cdmnk
    16.11.2016 12:41

    Чем именно лучше чем

    T t = (T)System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)).ConvertTo()
    
    С двумя простенькими хелпер методами?


    1. shai_hulud
      16.11.2016 12:54

      Ну, по первых, не все типы имеют TypeConverter.

      Некоторые предоставляют только операторы преобразования (например: IntPtr или JToken из Json.NET).

      Другие только методы Parse ToString (например: IPAddress).

      Из некоторых типов просто не существует «валидных» преобразований, хотя они очевидны. Из int? в enum SystemColor?, где достаточно развернуть nullable типы и преобразовать содержимое.

      И что бы не писать длинный switch\case по преобразуемым типам, проще вызвать один метод Convert, который сам найдет подходящий способ преобразования и вызовет его. Плюс мой вариант использует боксинг только в нескольких случаях.


  1. mezastel
    16.11.2016 15:14

    Поскольку я пишу проект, который реализует схожие задачи, я бы посоветовал все же для очевидно-конвертируемых типов реализовать конверсионные варианты на этапе комиляции (механизмом Т4), т.к. использование reflection в глобальных масштабах, особенно когда нужно делать Invoke() (например на TryParse()), слишком дорого.


  1. shai_hulud
    16.11.2016 15:37

    >использование reflection в глобальных масштабах
    в методе Convert нет рефлексии. Ни капельки, только вызовы закешированных делегатов, которые по скорости сравнимы с виртуальными вызовами. Так что перфоманс будет достаточен для большинства задач.

    Про ваш проект, я не понял для чего он.


    1. roman_kashitsyn
      17.11.2016 12:51
      +1

      Про ваш проект, я не понял для чего он.

      Описание короткое, но в целом понятное: ищет код, который превращает значение на входе в значение на выходе. Затея сомнительная, но интересная.


  1. hVostt
    26.11.2016 11:56

    По-моему ценность библиотеки стремится к нулю. Она ничего не делает такого, чего нельзя сделать без этой библиотеки. Она существенно не уменьшает количество кода, который надо будет писать без неё. При необходимости для конкретных случаев пишется простенький хелпер, или класс, по крайне мере он будет всегда доступен в проекте и его можно поправить, доработать и улучшить. Ну и самое страшное. Предлагаемый хелпер может очень много какие типы конвертить, но не абсолютно все. Отсюда, его использование, это хождение по минному полю, знать поддерживаемые типы наизусть — не представляется возможным, каждый раз лазить на гитхаб в исходники, тоже не весёлое занятие. Читателя код, используемый этот конвертер будет поджидать недоумение, что ещё за конвертер? Что он может? Работает ли он как ожидается? А если способ конвертации изменится?

    В общем, как часть собственного проекта — куда ни шло. Как самостоятельная библиотека — плохо.