如何制作通用类型转换方法

时间:2009-01-16 21:37:22

标签: c# .net

我想做的是:

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的名字是转换?为什么不DBNull.IsDBNull()?

9 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:3)

一位开发者正好写了这个函数,我们发现它非常有用。

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

如果失败,它会搜索目标类型的构造函数,该构造函数将source-type作为参数,然后调用它。

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

作为性能优化,一旦找到转换一次,它就会将其保存在[type,type]&lt; ==&gt;的字典中。 [转换MethodInfo],以便后续调用不必进行广泛的反射搜索。

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

答案 2 :(得分:2)

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

用于检查dbnull ...我使用typeof(object)是DbNull ...

答案 3 :(得分:2)

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

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

答案 4 :(得分:1)

即使使用Convert.IsDBNull,也可以采用更好的方式进行检查:

if (!databaseField is DBNull)

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

答案 5 :(得分:0)

尝试使用泛型。这样您就不必进行所有必须执行的运行时类型检查(这些都是在编译时完成的。)

答案 6 :(得分:0)

你见过这个功能吗?:

Microsoft.VisualBasic.CType()

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

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

对于基本类型,请使用Convert.ChangeType

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

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