Расширение Convert.ChangeType для создания пользовательских типов по запросу
Учитывая класс:
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, если есть другой способ сделать это.
Update:
Причина, по которой я спрашиваю, заключается в том, что она выбрасывает и исключает:
Недействительный отбор из 'System.String' в 'JibbaJabba + Foo'.
и добавление оператора не решило проблему.
Ответы
Ответ 1
В соответствии с документацией MSDN:
Для успешной конверсии значение должен внедрить IConvertible интерфейса, потому что метод просто завершает вызов соответствующему IConvertible. Метод требует, чтобы преобразование ценности в Поддерживается тип преобразования.
Глядя на интерфейс IConvertible
, он имеет метод ToType
. Вы могли бы попробовать это, может быть? (Отказ от ответственности: у меня нет. Это просто мысль.)
Изменить. В вашем случае кажется, что вы хотите конвертировать из string
в 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>
В любом случае, удачи.
Ответ 2
Прямой отбор из строки не будет работать, как уже указывал Дэн Тао. Могли бы вы, возможно, реализовать свою собственную оболочку для строки и использовать ее? Что-то вроде
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));
Не знаю, полезная ли это идея, но в любом случае..
Ответ 3
Неявный оператор здесь не работает? Если 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;
}
}
Ответ 4
значение объекта1 = "один";
Значение Foo2 = (Foo) System.Convert.ChangeType(значение, typeof (Foo));
Edit:
Хорошо, если вы пытаетесь создать тип Foo из строки, вы можете использовать отражение:
String controlToCreate = "Foo";
Type typeofControl = Type.GetType(controlToCreate,true);