通用类型转换而不冒异常风险 [英] General type conversion without risking Exceptions

查看:25
本文介绍了通用类型转换而不冒异常风险的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个可以采用多种不同数据类型(任何实现 IComparable 的)的控件.

I am working on a control that can take a number of different datatypes (anything that implements IComparable).

我需要能够将这些与传入的另一个变量进行比较.

I need to be able to compare these with another variable passed in.

如果主要数据类型是日期时间,并且传递给我一个字符串,我需要

If the main datatype is a DateTime, and I am passed a String, I need to

  • 尝试将字符串转换为 DateTime 以执行日期比较.
  • 如果 String 无法转换为 DateTime,则进行 String 比较.

所以我需要一种通用的方法来尝试从任何类型转换为任何类型.很简单,.Net 为我们提供了 TypeConverter 类.

So I need a general way to attempt to convert from any type to any type. Easy enough, .Net provides us with the TypeConverter class.

现在,确定字符串是否可以转换为 DateTime 的最佳方法是使用异常.如果 ConvertFrom 引发异常,我知道我无法进行转换并且必须进行字符串比较.

Now, the best I can work out to do to determine if the String can be converted to a DateTime is to use exceptions. If the ConvertFrom raises an exception, I know I cant do the conversion and have to do the string comparison.

以下是我得到的最好的:

The following is the best I got :

        string theString = "99/12/2009";
        DateTime theDate = new DateTime ( 2009, 11, 1 );

        IComparable obj1 = theString as IComparable;
        IComparable obj2 = theDate as IComparable;

        try
        {
            TypeConverter converter = TypeDescriptor.GetConverter ( obj2.GetType () );
            if ( converter.CanConvertFrom ( obj1.GetType () ) )
            {
                Console.WriteLine ( obj2.CompareTo ( converter.ConvertFrom ( obj1 ) ) );
                Console.WriteLine ( "Date comparison" );
            }
        }
        catch ( FormatException )
        {
            Console.WriteLine ( obj1.ToString ().CompareTo ( obj2.ToString () ) );
            Console.WriteLine ( "String comparison" );
        }

我们的部分工作标准规定:

Part of our standards at work state that :

只有在出现异常情况时才应引发异常 - 即.遇到错误.

但这不是一个例外情况.我需要另一种解决方法.

But this is not an exceptional situation. I need another way around it.

大多数变量类型都有一个 TryParse 方法,该方法返回一个布尔值允许您确定转换是否成功.但是 TypeConverter 没有可用的 TryConvert 方法.CanConvertFrom 仅在可以转换的情况下进行在这些类型之间,不考虑要转换的实际数据.IsValid 方法也没用.

Most variable types have a TryParse method which returns a boolean to allow you to determine if the conversion has succeeded or not. But there is no TryConvert method available to TypeConverter. CanConvertFrom only dermines if it is possible to convert between these types and doesnt consider the actual data to be converted. The IsValid method is also useless.

有什么想法吗?

编辑

我不能使用 AS 和 IS.我在编译时不知道这两种数据类型.所以我不知道要做什么和要做什么!!!

I cannot use AS and IS. I do not know either data types at compile time. So I dont know what to As and Is to!!!

编辑

好的,把这个混蛋钉死了.它不像 Marc Gravells 那样整洁,但它确实有效(我希望如此).感谢马克的灵感.当我有时间时会努力整理它,但我有一些我必须继续进行的错误修复.

Ok nailed the bastard. Its not as tidy as Marc Gravells, but it works (I hope). Thanks for the inpiration Marc. Will work on tidying it up when I get the time, but I've got a bit stack of bugfixes that I have to get on with.

    public static class CleanConverter
    {
        /// <summary>
        /// Stores the cache of all types that can be converted to all types.
        /// </summary>
        private static Dictionary<Type, Dictionary<Type, ConversionCache>> _Types = new Dictionary<Type, Dictionary<Type, ConversionCache>> ();

        /// <summary>
        /// Try parsing.
        /// </summary>
        /// <param name="s"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool TryParse ( IComparable s, ref IComparable value )
        {
            // First get the cached conversion method.
            Dictionary<Type, ConversionCache> type1Cache = null;
            ConversionCache type2Cache = null;

            if ( !_Types.ContainsKey ( s.GetType () ) )
            {
                type1Cache = new Dictionary<Type, ConversionCache> ();

                _Types.Add ( s.GetType (), type1Cache );
            }
            else
            {
                type1Cache = _Types[s.GetType ()];
            }

            if ( !type1Cache.ContainsKey ( value.GetType () ) )
            {
                // We havent converted this type before, so create a new conversion
                type2Cache = new ConversionCache ( s.GetType (), value.GetType () );

                // Add to the cache
                type1Cache.Add ( value.GetType (), type2Cache );
            }
            else
            {
                type2Cache = type1Cache[value.GetType ()];
            }

            // Attempt the parse
            return type2Cache.TryParse ( s, ref value );
        }

        /// <summary>
        /// Stores the method to convert from Type1 to Type2
        /// </summary>
        internal class ConversionCache
        {
            internal bool TryParse ( IComparable s, ref IComparable value )
            {
                if ( this._Method != null )
                {
                    // Invoke the cached TryParse method.
                    object[] parameters = new object[] { s, value };
                    bool result = (bool)this._Method.Invoke ( null,  parameters);

                    if ( result )
                        value = parameters[1] as IComparable;

                    return result;
                }
                else
                    return false;

            }

            private MethodInfo _Method;
            internal ConversionCache ( Type type1, Type type2 )
            {
                // Use reflection to get the TryParse method from it.
                this._Method = type2.GetMethod ( "TryParse", new Type[] { type1, type2.MakeByRefType () } );
            }
        }
    }

推荐答案

可以选择泛型吗?这是一个厚颜无耻的 hack,它寻找 TryParse 方法并通过(缓存的)委托调用它:

Are generics an option? Here's a cheeky hack that hunts the TryParse method and calls it via a (cached) delegate:

using System;
using System.Reflection;

static class Program
{
    static void Main()
    {
        int i; float f; decimal d;
        if (Test.TryParse("123", out i)) {
            Console.WriteLine(i);
        }
        if (Test.TryParse("123.45", out f)) {
            Console.WriteLine(f);
        }
        if (Test.TryParse("123.4567", out d)) {
            Console.WriteLine(d);
        }
    }
}
public static class Test
{
    public static bool TryParse<T>(string s, out T value) {
        return Cache<T>.TryParse(s, out value);
    }
    internal static class Cache<T> {
        public static bool TryParse(string s, out T value)
        {
            return func(s, out value);
        }    
        delegate bool TryPattern(string s, out T value);
        private static readonly TryPattern func;
        static Cache()
        {
            MethodInfo method = typeof(T).GetMethod(
                "TryParse", new Type[] { typeof(string), typeof(T).MakeByRefType() });
            if (method == null) {
                if (typeof(T) == typeof(string))
                    func = delegate(string x, out T y) { y = (T)(object)x; return true; };
                else
                    func = delegate(string x, out T y) { y = default(T); return false; };
            } else {
                func = (TryPattern) Delegate.CreateDelegate(typeof(TryPattern),method);
            }            
        }
    }
}

这篇关于通用类型转换而不冒异常风险的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆