расширение Convert.ChangeType для создания пользовательских типов по запросу - PullRequest
16 голосов
/ 03 октября 2010

С учетом класса:

public class Foo
{
    public string Name { get; set; }
}

Возможно ли создать экземпляр Foo из строки через Convert.ChangeType:

Type type = typeof(Foo);
object value = "one";

value = System.Convert.ChangeType(value, type);

Это сторонний APIпытаясь восстановить объекты.Кто-то упомянул, что это возможно с неявными операторами, но из моего понимания, которое позволит мне сделать следующее, а не создавать объект:

Foo foo = new Foo() { Name = "one" };
string fooAsString = foo;  // implicit conversion -- no cast needed

Есть ли способ создать объект таким образом?Кроме того, у меня есть возможность изменить Convert.ChangeType, если есть другой способ сделать это.

Обновление: Причина, по которой я спрашиваю, состоит в том, что он вызывает и исключение:

Неправильное приведение из 'System.String' к 'JibbaJabba + Foo'.

и добавление оператора не решило проблему.

Ответы [ 4 ]

20 голосов
/ 03 октября 2010

Согласно документации MSDN :

Чтобы преобразование было успешным, значение должно реализовывать интерфейс IConvertible, поскольку метод просто переносит вызовсоответствующему IConvertible методу.Метод требует, чтобы поддерживалось преобразование значения в translationType .

Если смотреть на интерфейс IConvertible, он имеет ToType метод.Вы могли бы попробовать это, может быть?(Отказ от ответственности: у меня нет. Это всего лишь мысль.)

Редактировать : В вашем случае кажется, что вы хотите преобразовать из astring до a Foo.Поскольку тип string (очевидно) не определяет преобразование в Foo в его реализации IConvertible, я считаю, что вам не повезло.


Обновление Я не хочу утверждать, что именно так вы должны всегда подходить к такого рода проблемам, но ...

Я посмотрел на код для Convert.ChangeType в Reflector ,Это долго;Я не буду воспроизводить это здесь.Но в основном он работает так, как сказано в документации: он только работает, если:

  • Параметр value является ненулевым экземпляром типа, который реализует IConvertible, или:
  • Тип параметра value и параметра conversionType совпадают (поэтому: Convert.ChangeType(myFoo, typeof(Foo)) также будет работать, хотя это будет довольно бесполезно).

Затем он циклически перебирает все типы, поддерживаемые IConvertible (который явно не включает в себя какие-либо определяемые пользователем типы), и в конечном итоге использует ToType в качестве запасного варианта.

Такнам нужно взглянуть на реализацию string типа ToType.

К сожалению, это одна неудачная строка:

return Convert.DefaultToType(this, type, provider);

Что делает DefaultToType?Точно то же самое, что и ChangeType (минус резервный ToType, очевидно, чтобы избежать бесконечной рекурсии).

Так что это просто не сработает.

Если выабсолютно привязанный к этой сторонней библиотеке, которая использует Convert.ChangeType за кулисами, я бы порекомендовал связаться с разработчиком библиотеки и попросить их каким-то образом расширить их API, что позволит вам выполнить то, что вы пытаетесь достичь.Возможны следующие варианты:

  • Принятие необязательного параметра делегата Converter<string, T> или Func<string, T>, как предложил Бен Фойгт в комментарии.
  • Принятие TypeConverter параметр
  • Прием параметра некоторого типа, который реализует интерфейс, такой как IParser<T>

В любом случае, удачи.

5 голосов
/ 03 октября 2010

Прямое приведение из строки не будет работать, как уже указывал Дэн Тао. Не могли бы вы реализовать свой собственный упаковщик для строки и использовать его? Что-то вроде

class MyString: IConvertible {

  public string Value { get; set; }

  ...
  object IConvertible.ToType(Type conversionType, IFormatProvider provider) {
    if (conversionType == typeof(Foo))
      return new Foo { Name = Value };
    throw new InvalidCastException();
  }

}
...
MyString myString = new MyString{Value="one"};
Foo myFoo = (Foo)Convert.ChangeType(myString, typeof(Foo));

Не знаю, полезная ли это идея, но все равно ..

1 голос
/ 03 октября 2010

Неявный оператор не будет работать здесь? Если Foo - это класс, который вы можете изменить, то в прошлом я использовал нечто подобное, что также позволяет сравнивать экземпляры Foo со строкой.

public class Foo
{
  public string Name { get; set; }

  public static implicit operator Foo(string value)
  {
    return new Foo { Name = value };
  }
}

...
Foo foo = "fooTest";
Console.WriteLine("Foo name: {0}", foo.Name);
...

Редактировать: Если вы должны использовать ChangeType, то, насколько я знаю, вам не повезло. Если вы можете изменить API для использования TypeConverter, вы можете использовать что-то вроде следующего.

...
Type type = typeof(Foo);
object value = "one";

var converter = TypeDescriptor.GetConverter(type);
if (converter.CanConvertFrom(value.GetType()))
{
  object newObject = converter.ConvertFrom(value);
  Console.WriteLine("Foo value: {0}", newObject.ToString());
}
...

public class FooConverter : TypeConverter
{
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  {
    return sourceType == typeof(string);
  }

  public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  {
    var name = value as string;
    if (name != null)
      return new Foo { Name = name };
    else
      return base.ConvertFrom(context, culture, value);
  }
}

[TypeConverter(typeof(FooConverter))]
public class Foo
{
  public string Name { get; set; }

  public override string ToString()
  {
    return Name;
  }
}
0 голосов
/ 03 октября 2010

object value1 = "one";

Foo value2 = (Foo) System.Convert.ChangeType (value, typeof (Foo));

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


Хорошо, тогда, если вы пытаетесь создать тип Foo из строки, вы можете использовать отражение:

String controlToCreate = "Foo";
Type typeofControl = Type.GetType(controlToCreate,true);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...