【问题标题】:How do I make a universal type conversion method如何制作通用类型转换方法
【发布时间】:2009-01-18 06:37:58
【问题描述】:

我想做的是:

bool Convert( out Object output, Object source)
{
   // find type of output.
   // convert source to that type if possible
   // store result in output.

   return success
} 

有可能吗?

显然,有一个强大的大规模“if”构造可以工作,但这需要为每种可能的数据类型编写一个 if 块。即使假设我们将其限制为原语和字符串,它仍然是一大块代码。我正在考虑一些更具反思性的事情。

顺便说一句:在浏览 api 时,我遇到了 Convert.IsDBNull() 方法,这将为我节省很多

 if ( !databasefield.GetType().Equals( DBNull.Value ) )

为什么以 G-d 的名义在 Convert 中?为什么不 DBNull.IsDBNull() ?

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    这是我使用的一个示例,您可以通过注册其他类型转换器将其他复杂的转换注入其中。

    public static class Converter
    {
        public static T Convert<T>(object obj, T defaultValue)
        {
            if (obj != null)
            {
                if (obj is T)
                {
                    return (T)obj;
                }
    
                TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
    
                if (converter.CanConvertFrom(obj.GetType()))
                {
                    return (T)converter.ConvertFrom(obj);
                }
            }
    
            return defaultValue;
        }
    

    【讨论】:

    • 这在大多数情况下不起作用,因为没有多少类型具有与字符串以外的其他类型相互转换的转换器。
    • 这就是为什么我说你可以注册其他转换器。
    • 是的,你说过对于复杂的转换,但这甚至不能处理像 int 到 string 这样的简单转换。
    • @Patrik,如果您想要原始转换,请使用 Convert 类。
    【解决方案2】:

    一位开发人员正是编写了这个函数,我们发现它非常有用。

    本质上,它使用反射来搜索两种类型之间的隐式转换(搜索“op_Implicit”以获取更多信息)。

    如果失败,它会搜索目标类型的构造函数,该构造函数将源类型作为参数,并调用它。

    如果失败,它会搜索可以将一种类型解析为另一种类型的 Parse 方法。这将找到诸如 Int32.Parse 之类的东西以从 String 转换为 Int,或 IPAddress.Parse 以从 String 转换为 IPAddress。

    作为性能优化,一旦找到一次转换,就将其保存在 [type,type] [conversion MethodInfo] 的字典中,以便后续调用不必经过广泛的反射搜索.

    这几乎可以很好地处理所有类型转换。

    【讨论】:

      【解决方案3】:

      我多次遇到您的问题。我一直发现构建和使用转换函数的时间抵消了它节省的时间。最终会出现精度和舍入等问题,您仍然需要处理特殊情况。

      为了检查 dbnull... 我使用 typeof (object) is DbNull ...

      【讨论】:

        【解决方案4】:

        这是一个有趣的小练习!我刚刚写了这个,所以如果它不起作用,请不要挂我,但在这里我尝试使用我现在能想到的不同方法进行转换。

        public static class Converter
        {
            public static bool TryConvert<T>(object o, out T result)
            {
                if (o == null && typeof(T).IsClass)
                {
                    result = default(T);
                    return true;
                }
        
                var convertible = o as IConvertible;
                if (convertible != null && ConvertibleHandlesDestinationType<T>())
                {
                    result = (T)Convert.ChangeType(convertible, typeof(T));
                    return true;
                }
        
                if (o != null)
                {
                    if (typeof(T).IsAssignableFrom(o.GetType()))
                    {
                        result = (T)o;
                        return true;
                    }
        
                    var converter = TypeDescriptor.GetConverter(o);
                    if (converter.CanConvertTo(typeof(T)))
                    {
                        result = (T)converter.ConvertTo(o, typeof(T));
                        return true;
                    }
                }
        
                result = default(T);
                return false;
            }
        
            private static bool ConvertibleHandlesDestinationType<T>()
            {
                return 
                    typeof(T).Equals(typeof(Boolean)) ||
                    typeof(T).Equals(typeof(Byte)) ||
                    typeof(T).Equals(typeof(char)) ||
                    typeof(T).Equals(typeof(DateTime)) ||
                    typeof(T).Equals(typeof(Decimal)) ||
                    typeof(T).Equals(typeof(Double)) ||
                    typeof(T).Equals(typeof(Int16)) ||
                    typeof(T).Equals(typeof(Int32)) ||
                    typeof(T).Equals(typeof(Int64)) ||
                    typeof(T).Equals(typeof(SByte)) ||
                    typeof(T).Equals(typeof(Single)) ||
                    typeof(T).Equals(typeof(string)) ||
                    typeof(T).Equals(typeof(UInt16)) ||
                    typeof(T).Equals(typeof(UInt32)) ||
                    typeof(T).Equals(typeof(UInt64));
            }
        }
        

        由于输出参数的类型是 T,我们可以使用类型推断,因此调用看起来像:

        int number;
        if (Converter.TryConvert("123", out number))
        {
            Debug.WriteLine(number);
        }
        

        【讨论】:

        • 如果您要遵循 DateTime.TryParse 和类似的语法,我建议您将整个方法包装在 try/catch 中,然后让 out default(T) 返回 false,因为 TryParse 方法期望他们永远不会抛出异常。
        【解决方案5】:

        即使使用 Convert.IsDBNull,也有更好的检查方法:

        if (!databaseField is DBNull)
        

        另外请注意,您可以在 Type 上使用 ==,因为特定类型只有一个 Type 实例。

        【讨论】:

        • 我知道 == 问题。但是来自 Java 背景,我从来没有习惯使用它。我对“is”或“instanceof”也有恐惧症,我只是不信任二进制(双操作数)关键字。
        • 您也可以在 Java 中将 == 与 Class 一起使用 - 同样,因为类表示只有一个对象。我强烈建议您努力克服恐惧症。为什么不应该“信任”二元关键字运算符?您的代码因此受到影响。
        【解决方案6】:

        没有皈依的圣杯。对于 m 类型,您需要 m*(m-1) 转换例程来涵盖所有排列。

        对于原始类型,使用 Convert.ChangeType

        如果一个类型可以从原始类型转换,它可以实现 IConvertable 接口并从 Convert 类中使用。

        对于其他一切,@Brian Rudolfs 的答案是最好的。为您需要的每个排列注册一个显式转换方法。

        【讨论】:

          【解决方案7】:

          尝试使用泛型。这样一来,您就不必执行原本必须执行的所有运行时类型检查(而是在编译时完成)。

          【讨论】:

            【解决方案8】:

            你见过这个功能吗?:

            Microsoft.VisualBasic.CType()
            

            【讨论】:

            • Ctype 不是函数,是关键字,因此您不能在其他语言中使用它(OP 似乎要求使用 C#)。编译器将其替换为适当的转换(通常是 Microsoft.VisualBasic.Conversions.* 用于原始类型)。
            【解决方案9】:

            我在博客 Working with DataTables/Datarows 上写了一篇关于如何管理 DataRow 类型转换的博文

            ///<summary>
            /// Extension methods for manipulating DataRows
            ///</summary>
            public static class DataRowUserExtensions
            {
                /// <summary>
                /// Determines whether [is null or empty string] [the specified data row].
                /// </summary>
                /// <param name="dataRow">The data row.</param>
                /// <param name="key">The key.</param>
                /// <returns>
                ///   <c>true</c> if [is null or empty string] [the specified data row]; otherwise, <c>false</c>.
                /// </returns>
                public static bool IsNullOrEmptyString(this DataRow dataRow, string key)
                {
                    if (dataRow.Table.Columns.Contains(key))
                        return dataRow[key] == null || dataRow[key] == DBNull.Value || dataRow[key].ToString() == string.Empty;
            
                    throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
                }
            
                /// <summary>
                /// Gets the specified data row.
                /// </summary>
                /// <typeparam name="T"></typeparam>
                /// <param name="dataRow">The data row.</param>
                /// <param name="key">The key.</param>
                /// <returns></returns>
                public static T Get<T>(this DataRow dataRow, string key)
                {
                    if (dataRow.Table.Columns.Contains(key))
                        return dataRow.IsNullOrEmptyString(key) ? default(T) : (T) ChangeTypeTo<T>(dataRow[key]);
            
                    throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
                }
            
                /// <summary>
                /// Changes the type to.
                /// </summary>
                /// <typeparam name="T"></typeparam>
                /// <param name="value">The value.</param>
                /// <returns></returns>
                private static object ChangeTypeTo<T>(this object value)
                {
                    if (value == null)
                        return null;
            
                    Type underlyingType = typeof (T);
                    if (underlyingType == null)
                        throw new ArgumentNullException("value");
            
                    if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition().Equals(typeof (Nullable<>)))
                    {
                        var converter = new NullableConverter(underlyingType);
                        underlyingType = converter.UnderlyingType;
                    }
            
                    // Guid convert
                    if (underlyingType == typeof (Guid))
                    {
                        return new Guid(value.ToString());
                    }
            
                    // Do conversion
                    return underlyingType.IsAssignableFrom(value.GetType()) ?
                          Convert.ChangeType(value, underlyingType)
                        : Convert.ChangeType(value.ToString(), underlyingType);
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-12-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-04-04
              • 1970-01-01
              • 2018-08-09
              • 1970-01-01
              相关资源
              最近更新 更多