【问题标题】:C#: Dynamic parse from System.TypeC#:来自 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

标签: c# .net type-conversion


【解决方案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】:

      技术上不可能查看一个字符串,并确定它代表哪种类型。

      因此,对于任何通用方法,您至少需要:

      1. 要解析的字符串
      2. 用于解析的类型。

      看看静态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 链,并在找到匹配的值时返回该值。您仍然必须手动列出所有可能的类型,但您只需要做一次。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-06
        • 2020-06-17
        • 1970-01-01
        • 2022-07-02
        相关资源
        最近更新 更多