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的想法)

C# 将动态 JSON JToken 解析为列表

来自运行时数据层的 C# 中的动态 odata 服务

C# JToken类,实现解析动态json数据遍历查找

解析来自 API 的动态 jsonResult 响应给出“对象不包含 X 的定义”

C# WPF后台代码动态添加控件