【问题标题】:extending Convert.ChangeType to produce user-defined types on request扩展 Convert.ChangeType 以根据请求生成用户定义的类型
【发布时间】:2011-04-20 07:59:42
【问题描述】:

给定班级:

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

是否可以通过 Convert.ChangeType 从字符串创建 Foo 实例:

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

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

这就是第 3 方 API 尝试重建对象的方式。有人提到这可以通过隐式运算符实现,但据我了解,这将让我执行以下操作,而不是创建对象:

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

有没有办法以这种方式创建对象?此外,如果有其他方法,我确实可以更改 Convert.ChangeType。

更新: 我问的原因是因为它抛出异常:

从 'System.String' 到的无效转换 'JibbaJabba+Foo'。

添加运算符并没有解决问题。

【问题讨论】:

  • 我应该注意我尝试了隐式运算符但它不起作用。
  • 请注意,您可以在任一方向使用隐式运算符,但它仍然是 C# 编译器功能,不会被已编译的代码使用。很可能您必须查看 3rd 方库是否允许您自己创建对象,也许存在接受 Converter<string, T> 委托的重载?

标签: c# type-conversion


【解决方案1】:

根据the MSDN documentation

为了使转换成功, 必须实现 IConvertible 接口,因为方法简单 包装对适当的调用 IConvertible 方法。方法 需要将 value 转换为 支持conversionType

IConvertible接口,它有一个ToType方法。你可以试试,也许? (免责声明:我没有。这只是一个想法。)

编辑:在您的情况下,您似乎想将 string 转换为 Foo。由于string 类型(显然)在其IConvertible 实现中没有定义到Foo 的转换,我相信你不走运。


更新:我不想建议你总是应该这样处理这类问题,但是......

我查看了ReflectorConvert.ChangeType 的代码。它的长;我不会在这里复制它。但基本上它就像文档所说的那样:它在以下情况下有效:

  • value 参数是实现IConvertible 的类型的非空实例,或者:
  • value 参数的类型和conversionType 参数的类型相同(所以:Convert.ChangeType(myFoo, typeof(Foo)) 也可以工作,虽然它没什么用处)。

然后,它循环遍历IConvertible 支持的所有类型(显然不包括任何用户定义的类型)并最终使用ToType 作为后备。

所以,我们需要看看string 类型对ToType 的实现。

不幸的是,这是一条不幸的线:

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

DefaultToType 是做什么的?与ChangeType 完全相同(减去ToType 后备,显然是为了避免无限递归)。

所以这根本行不通。

如果您完全依赖于在幕后使用 Convert.ChangeType 的第 3 方库,我建议您联系该库的开发人员并要求他们以某种方式扩展他们的 API,以便您完成自己的任务试图完成。一些可能性可能是:

  • 按照 Ben Voigt 在评论中的建议,接受可选的 Converter<string, T>Func<string, T> 委托参数。
  • 接受TypeConverter 参数
  • 接受实现类似IParser<T> 的接口的某种类型的参数

无论如何,祝你好运。

【讨论】:

  • 我看到了,在 MSDN 中很好地解释了为什么会发生异常。看起来我被水洗了,必须找到另一种方法。我会 +1 并让问题保持开放状态,看看是否有其他人有想法。
  • @blu:是的,你永远不知道。有时文档只揭示了故事的一部分。
【解决方案2】:

正如 Dan Tao 已经指出的那样,从字符串直接转换是行不通的。您能否为字符串实现自己的包装器并使用它?类似的东西

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

不知道这是否有用,但无论如何..

【讨论】:

  • 这是一个有趣的想法,但我有一种感觉 OP 尝试使用的第 3 方库需要字符串作为输入(例如,它可能是一个反序列化库)。
  • 是的,我也有同样的感觉 :)
【解决方案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;
  }
}

【讨论】:

  • 我认为 OP 了解隐式运算符的工作原理。他试图做的是设计一种类型,当第 3 方库在字符串上调用 Convert.ChangeType 并将他的自定义类型作为 type 参数传递时,将实例化他的自定义类型的一个实例。换句话说,Convert.ChangeType 是此代码必须通过的途径(基于 OP 的要求)。
  • 我明白你的意思。从他的评论中不清楚,“另外,如果有其他方法,我确实有能力更改 Convert.ChangeType。” 这是否意味着他可以修改 API 源。
【解决方案4】:

对象值1 = "一";

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

编辑:


那么,如果您尝试从字符串创建 Foo 类型,您可以使用反射:

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

【讨论】:

  • 更新也不起作用。答案的第二部分与问题无关。
  • OP 特别希望与ChangeType 一起工作,因为它正在被第 3 方库使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-21
  • 2017-01-22
  • 2021-01-07
  • 2021-04-08
相关资源
最近更新 更多