简体   繁体   English

扩展Convert.ChangeType以根据请求生成用户定义的类型

[英]extending Convert.ChangeType to produce user-defined types on request

Given the class: 鉴于课程:

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

Is it possible to have a Foo instance created from a string through Convert.ChangeType: 是否可以通过Convert.ChangeType从字符串创建Foo实例:

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

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

This is how a 3rd party API is attempting to rebuild objects. 这是第三方API尝试重建对象的方式。 Someone mentioned this is possible with implicit operators, but from my understanding that will let me do the following, not create the object: 有人提到这可能是隐式运算符,但从我的理解,我将执行以下操作,而不是创建对象:

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

Is there a way to create the object this way? 有没有办法以这种方式创建对象? Also, I do have the ability to change the Convert.ChangeType if there is another way to do this. 此外,如果有另一种方法,我可以更改Convert.ChangeType。

Update: The reason I am asking is because it throws and exception: 更新:我问的原因是因为它抛出异常:

Invalid cast from 'System.String' to 'JibbaJabba+Foo'. 从'System.String'到'JibbaJabba + Foo'的转换无效。

and adding the operator did not resolve the issue. 并添加运营商没有解决问题。

According to the MSDN documentation : 根据MSDN文档

For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. 要使转换成功, value必须实现IConvertible接口,因为该方法只是调用适当的IConvertible方法。 The method requires that conversion of value to conversionType be supported. 该方法要求支持将 转换conversionType

Looking at the IConvertible interface, it has a ToType method. 查看 IConvertible接口,它有一个 ToType方法。 You could try that, maybe? 你可以试试吗,也许吧? (Disclaimer: I haven't. It's just a thought.) (免责声明:我没有。这只是一个想法。)

Edit : In your case, it seems that you want to convert from a string to a Foo . 编辑 :在您的情况下,似乎您想要 string 转换Foo Since the string type (obviously) does not define a conversion to Foo in its IConvertible implementation, I believe you're out of luck. 由于string类型(显然)没有在其IConvertible实现中定义到FooIConvertible ,我相信你运气不好。


Update : I don't want to suggest that this is how you should always approach this sort of problem, but... 更新 :我不想暗示这是你应该如何处理这类问题,但......

I took a look at the code for Convert.ChangeType in Reflector . 我看了一下Reflector中 Convert.ChangeType的代码。 It's long; 它的长; I won't reproduce it here. 我不会在这里重现它。 But basically it's doing as the documentation says: it only works if: 但基本上它正如文档所说: 它只适用于:

  • The value parameter is a non-null instance of a type that implements IConvertible , or: value参数是实现IConvertible的类型的非null实例,或者:
  • The type of the value parameter and the conversionType parameter are the same (so: Convert.ChangeType(myFoo, typeof(Foo)) would also work, though it'd be pretty useless). value参数的类型和conversionType参数是相同的(所以: Convert.ChangeType(myFoo, typeof(Foo))也可以工作,虽然它很无用)。

Then, it cycles through all the types supported by IConvertible (which obviously does not include any user-defined types) and ultimately uses ToType as a fallback. 然后,它循环遍历IConvertible支持的所有类型(显然不包括任何用户定义的类型),并最终使用ToType作为后备。

So, we need to look at the string type's implementation of ToType . 因此,我们需要查看string类型的ToType实现。

Sadly, it is one unfortunate line: 可悲的是,这是一个不幸的路线:

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

What does DefaultToType do? DefaultToType做什么? Exactly the same thing as ChangeType (minus the ToType fallback, obviously to avoid infinite recursion). ChangeType完全相同(减去ToType回退,显然是为了避免无限递归)。

So this just simply isn't going to work. 所以这根本就不会起作用。

If you're absolutely tied to this 3rd party library that's using Convert.ChangeType behind the scenes, I would recommend contacting the library's developer and asking them to extend their API in some way that will allow you to accomplish what you're trying to accomplish. 如果您完全依赖于在幕后使用Convert.ChangeType第三方库,我建议您联系图书馆的开发人员并要求他们以某种方式扩展他们的API,以便您完成您要完成的任务。 Some possiblities might be: 一些可能性可能是:

  • Accepting an optional Converter<string, T> or Func<string, T> delegate parameter, as suggested by Ben Voigt in a comment. 接受可选的Converter<string, T>Func<string, T> delegate参数,如Ben Voigt在评论中所建议的那样。
  • Accepting a TypeConverter parameter 接受TypeConverter参数
  • Accepting a parameter of some type that implements an interface like IParser<T> 接受某种类型的参数来实现IParser<T>类的接口

Anyway, best of luck. 无论如何,祝你好运。

Direct cast from string won't work as Dan Tao already pointed out. 正如丹涛已经指出的那样,从弦上直接投射不会起作用。 Could you maybe implement your own wrapper for the string and use that? 你可以为字符串实现自己的包装并使用它吗? Something like 就像是

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));

Don't know if it is a useful idea but anyway.. 不知道这是否是一个有用的想法,但无论如何..

An implicit operator won't work here? 隐式运算符在这里不起作用? If Foo is a class you can modify then I've used something like this in the past, which also allows you to compare Foo instances to a string. 如果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);
...

Edit: If you must use ChangeType then as far as I know you're out of luck. 编辑:如果你必须使用ChangeType,那么据我所知你运气不好。 If you can modify the API to use a TypeConverter, you can use something like the following. 如果您可以修改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;
  }
}

object value1 = "one"; object value1 =“one”;

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

Edit: 编辑:


Ok then, if you are trying to create type of Foo from string, you could use reflection: 那么,如果您尝试从字符串创建Foo类型,则可以使用反射:

String controlToCreate = "Foo";
Type typeofControl = Type.GetType(controlToCreate,true);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM