C#:来自 System.Type 的动态解析
Posted
技术标签:
【中文标题】C#:来自 System.Type 的动态解析【英文标题】:C#: Dynamic parse from System.Type 【发布时间】:2011-01-23 17:46:30 【问题描述】:我有一个类型、一个字符串和一个对象。
有什么方法可以调用解析方法或动态转换字符串上的那种类型吗?
基本上如何删除此逻辑中的 if 语句
object value = new object();
String myString = "something";
Type propType = p.PropertyType;
if(propType == Type.GetType("DateTime"))
value = DateTime.Parse(myString);
if (propType == Type.GetType("int"))
value = int.Parse(myString);
做一些类似这样的事情。
object value = new object();
String myString = "something";
Type propType = p.PropertyType;
//this doesn't actually work
value = propType .Parse(myString);
【问题讨论】:
你没有说明p
是如何定义的,错别字?
至少,您应该使用is
运算符。我已经更新了您的问题,以便在不使用反射的情况下正确检查类型。
@David Pfeffer, is
运算符应用不正确。 is
在这种情况下永远不会返回 true [propType
将始终是 Type
] 类型。你想使用propType == typeof(DateTime)
我有类型。我留下了我实例化 p 的代码。但让我们假设我拥有它。
呵呵,不用担心,我发现它是因为我自己已经做过很多次了;)也会修改它以使用typeof
,但我把我的代表花在了一个赏金问题上:P
【参考方案1】:
TypeDescriptor
来救援!:
var converter = TypeDescriptor.GetConverter(propType);
var result = converter.ConvertFrom(myString);
所有原始类型(加上 Nullable<TPrimitive>
和许多其他内置类型)都已集成到 TypeConverter 基础架构中,因此支持“开箱即用”。
要将自定义类型集成到TypeConverter
基础架构中,请实现您自己的TypeConverter
并使用TypeConverterAttribute
来装饰要转换的类,并使用您的新TypeConverter
【讨论】:
你会如何用这个属性装饰 DateTime 或者它是默认装饰的? 由于您需要装饰要转换的类,这对于他试图为内置类型执行此操作的示例是否不起作用? 哇,酷。不知道TypeDescriptor
。你能详细说明一下用途吗?也许带我们通过框架值类型和复杂自定义类型的具体用法?我非常想看看要融入多少工作
谢谢,我刚刚删除了很多行,因为我不小心实现了一个大型 Converter 类来完全按照您在此处所做的操作。
不错!很高兴知道。为清楚起见,您可以添加它适用于开箱即用的简单框架类型。 (见拉撒路评论)。【参考方案2】:
我遇到了这个问题,我就是这样解决的:
value = myString;
var parse = propType.GetMethod("Parse", new[] typeof(string) );
if (parse != null)
value = parse.Invoke(null, new object[] value );
...它对我有用。
总而言之,您试图在对象类型上找到一个静态“Parse”方法,该方法只接受一个字符串作为参数。如果您找到这样的方法,请使用您尝试转换的字符串参数调用它。由于 p 是我的类型的 PropertyInfo,我通过将我的实例设置为如下值来结束此方法:
p.SetValue(instance, value, null);
【讨论】:
【参考方案3】:取决于你想完成什么。
1) 如果您只是想清理代码并删除重复的类型检查,那么您要做的就是将检查集中在一个方法中,comme
public static T To<T> (this string stringValue)
T value = default (T);
if (typeof (T) == typeof (DateTime))
// insert custom or convention System.DateTime
// deserialization here ...
// ... add other explicit support here
else
throw new NotSupportedException (
string.Format (
"Cannot convert type [0] with value [1] to type [2]." +
" [2] is not supported.",
stringValue.GetType (),
stringValue,
typeof (T)));
return value;
2) 如果您想要更通用的基本类型,您可以尝试类似Thomas Levesquesuggests - 虽然事实上,我自己并没有尝试过,我不熟悉 [recent?] 对 @ 的扩展987654324@。也是一个很好的建议。
3) 事实上,您可能希望将上面的 1) 和 2) 合并到一个扩展中,使您能够支持基本的值转换和显式的复杂类型支持。
4) 如果您想完全“解放双手”,那么您也可以默认使用普通的旧反序列化 [Xml 或二进制,非此即彼]。当然,这会限制您的输入 - 即所有输入必须采用适当的 Xml 或二进制格式。老实说,这可能有点矫枉过正,但值得一提。
当然,所有这些方法本质上都做同样的事情。它们中的任何一个都没有魔法,在某些时候 someone 正在执行线性查找 [无论是通过顺序 if 子句的隐式查找还是通过 .Net 转换和序列化工具在幕后进行的查找] .
5) 如果您想提高性能,那么您要做的就是改进转换过程中的“查找”部分。创建一个明确的“支持的类型”列表,每种类型对应于数组中的一个索引。然后指定索引,而不是在调用中指定类型。
编辑:因此,虽然线性查找简洁而快速,但我也想到如果消费者简单地获取转换函数并直接调用它们会更快。也就是说,消费者知道它想转换成什么类型[this is a given],所以如果它需要一次转换很多项,
// S == source type
// T == target type
public interface IConvert<S>
// consumers\infrastructure may now add support
int AddConversion<T> (Func<S, T> conversion);
// gets conversion method for local consumption
Func<S, T> GetConversion<T> ();
// easy to use, linear look up for one-off conversions
T To<T> (S value);
public class Convert<S> : IConvert<S>
private class ConversionRule
public Type SupportedType get; set;
public Func<S, object> Conversion get; set;
private readonly List<ConversionRule> _map = new List<ConversionRule> ();
private readonly object _syncRoot = new object ();
public void AddConversion<T> (Func<S, T> conversion)
lock (_syncRoot)
if (_map.Any (c => c.SupportedType.Equals (typeof (T))))
throw new ArgumentException (
string.Format (
"Conversion from [0] to [1] already exists. " +
"Cannot add new conversion.",
typeof (S),
typeof (T)));
ConversionRule conversionRule = new ConversionRule
SupportedType = typeof(T),
Conversion = (s) => conversion (s),
;
_map.Add (conversionRule);
public Func<S, T> GetConversion<T> ()
Func<S, T> conversionMethod = null;
lock (_syncRoot)
ConversionRule conversion = _map.
SingleOrDefault (c => c.SupportedType.Equals (typeof (T)));
if (conversion == null)
throw new NotSupportedException (
string.Format (
"Conversion from [0] to [1] is not supported. " +
"Cannot get conversion.",
typeof (S),
typeof (T)));
conversionMethod =
(value) => ConvertWrap<T> (conversion.Conversion, value);
return conversionMethod;
public T To<T> (S value)
Func<S, T> conversion = GetConversion<T> ();
T typedValue = conversion (value);
return typedValue;
// private methods
private T ConvertWrap<T> (Func<S, object> conversion, S value)
object untypedValue = null;
try
untypedValue = conversion (value);
catch (Exception exception)
throw new ArgumentException (
string.Format (
"Unexpected exception encountered during conversion. " +
"Cannot convert [0] [1] to [2].",
typeof (S),
value,
typeof (T)),
exception);
if (!(untypedValue is T))
throw new InvalidCastException (
string.Format (
"Converted [0] [1] to [2] [3], " +
"not of expected type [4]. Conversion failed.",
typeof (S),
value,
untypedValue.GetType (),
untypedValue,
typeof (T)));
T typedValue = (T)(untypedValue);
return typedValue;
它会被用作
// as part of application innitialization
IConvert<string> stringConverter = container.Resolve<IConvert<string>> ();
stringConverter.AddConversion<int> (s => Convert.ToInt32 (s));
stringConverter.AddConversion<Color> (s => CustomColorParser (s));
...
// a consumer elsewhere in code, say a Command acting on
// string input fields of a form
//
// NOTE: stringConverter could be injected as part of DI
// framework, or obtained directly from IoC container as above
int someCount = stringConverter.To<int> (someCountString);
Func<string, Color> ToColor = stringConverter.GetConversion <Color> ();
IEnumerable<Color> colors = colorStrings.Select (s => ToColor (s));
我更喜欢后一种方法,因为它让您完全控制转换。如果您使用控制反转 [IoC] 容器(如 Castle Windsor 或 Unity),则此服务的注入已为您完成。此外,因为它是基于 instance 的,所以您可以拥有多个实例,每个实例都有自己的一组转换规则 - 例如,如果您有多个用户控件,每个都生成自己的 DateTime
或其他复杂的字符串格式。
哎呀,即使你想支持单个目标类型的多个转换规则,这也是可能的,你只需要扩展方法参数来指定哪个。
【讨论】:
【参考方案4】:技术上不可能查看一个字符串,并确定它代表哪种类型。
因此,对于任何通用方法,您至少需要:
-
要解析的字符串
用于解析的类型。
看看静态Convert.ChangeType()
方法。
【讨论】:
从技术上讲,他并没有尝试从字符串中推断对象类型。他被赋予了对象类型,并希望从一个表示 [字符串] 转到另一个 [定义明确的对象实例]【参考方案5】:这应该适用于所有原始类型,以及实现 IConvertible
的类型
public static T ConvertTo<T>(object value)
return (T)Convert.ChangeType(value, typeof(T));
编辑:实际上在您的情况下,您不能使用泛型(至少不容易)。相反,您可以这样做:
object value = Convert.ChangeType(myString, propType);
【讨论】:
如何从 Type propType 到 propType.Parse(myString)?我很好奇这是如何工作的。 非常感谢!我正在寻找类似的东西, > 成功了。【参考方案6】:看起来您想要做的事情(至少如果所涉及的类型是您无法修改源的类型)需要duck typing,这不在 C# 中
如果您经常需要这样做,我会将逻辑包装在一个类或方法中,您可以将“myString”和“propType”传递给它,它会返回值。在该方法中,您只需执行上面的 if 链,并在找到匹配的值时返回该值。您仍然必须手动列出所有可能的类型,但您只需要做一次。
【讨论】:
以上是关于C#:来自 System.Type 的动态解析的主要内容,如果未能解决你的问题,请参考以下文章
c#:实现动态编译,并实现动态MutilProcess功能(来自python mutilprocess的想法)