• 深度解析 TypeConverter & TypeConverterAttribute (一)


    前言
        我们在开发复杂控件的时候不可避免的碰到类型转换TypeConverter,微软给我们提供了很多转换类如ArrayConverter,BaseNumberConverter,BooleanConverter(MSDN上更多:ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/cpref3/html/N_System_ComponentModel.htm)等直接或间接的继承了TypeConverter类。我们在类型转换的时候经常用到这些类。然而我们如何编写自定义的TypeConverter类呢,又怎么样在复杂控件中使用呢。

    TypeConverter Class 
        TypeConverter类就是将一种类型(object,可以说是任何类型)转换到另一种类型(一般为string),或者将另一种类型转换回来。所有继承TypeConverter类型的都必须实现4个方法:(这里以另一种类型string为例)
        CanConverterTo 有两个重载方法,
                 TypeConverter.CanConvertTo (Type) 
                 TypeConverter.CanConvertTo (ITypeDescriptorContext, Type) 
           都有一个Type参数(要转换成什么类型),例如我们设计的要转换成string,在方法体里面判断这个参数的Type如果是string,则返回true,否则返回           false
        ConverterTo 也有两重载,
                 TypeConverter.ConvertTo (Object, Type)
                 TypeConverter.ConvertTo (ITypeDescriptorContext, CultureInfo, Object, Type)
           都有Object和Type参数,将Object转成Type类型的Object,返回Type类型的Object。
      下面类似的两个方法,不过方向相反,是从其他类型装换回来。
        CanConverterFrom 重载,
                 TypeConverter.CanConvertFrom (Type) 
                 TypeConverter.CanConvertFrom (ITypeDescriptorContext, Type) 
           在方法体里面判断参数Type是不是能转换回来的类型,例如string类型,如果是返回true,否则返回false。
        ConverterFrom 重载,
                TypeConverter.ConvertFrom (Object) 
                TypeConverter.ConvertFrom (ITypeDescriptorContext, CultureInfo, Object) 
           在方法体里面判断参数Object的类型是不是能转换回来的类型,例如string类型,如果是返回转换回来的类型。
           
        举例说明,以GPS经纬度位置为例,经纬度位置GPSLocation包括复杂属性经度Longitude和纬度Latitude。现我们根据其一属性Longitude类写个LongtitudeTypeConverter类。
        首先我们得有个Longtitude类吧。

    public class Longitude
    {
        private int _Degrees;
        private int _Minutes;
        private int _Seconds;
        private LongitudeDirection _Direction;
    
        /// <summary>
        /// 度数
        /// </summary>
        public int Degrees { }
    
        /// <summary>
        /// 分度
        /// </summary>
        public int Minutes { }
    
        /// <summary>
        /// 秒读
        /// </summary>
        public int Seconds { }
    
        /// <summary>
        /// 方向
        /// </summary>
        public LongitudeDirection Direction
        {
        }
    }

    有了个这个类,我们怎样将其转换到string类或其他类呢(这里假设string类)例如“24W3'4”形式,也许你会说重写ToString()方法不就行了,似乎可行,但如果转换成其他类呢,又从其他类转换回来呢,怎么办。还有在复杂控件中Designer设计中又该怎么办。(在复杂控件的应用稍后介绍)
        自然,这样我们是不是要写个转换类比较好呢,这个类必须直接或这间接继承TypeConverter类。

    class LongitudeTypeConverter : TypeConverter
    {
    }

    然后重载实现上面说的四个方法,现在我要Longitude类转换到string类型


    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(string))
            return true;
        else
            return base.CanConvertFrom(context, sourceType);
    }
    
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if ((destinationType == typeof(string)) |
            (destinationType == typeof(InstanceDescriptor)))
            return true;
        else
            return base.CanConvertTo(context, destinationType);
    }
    
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        // check that the value we got passed on is of type Longitude
        if (value != null)
            if (!(value is Longitude))
                throw new Exception(WrongType);
    
        // convert to a string
        if (destinationType == typeof(string))
        {
            // no value so we return an empty string
            if (value == null)
                return string.Empty;
    
            // strongly typed
            Longitude LongValue = value as Longitude;
    
            // get the two type converters to use
            TypeConverter IntConverter = TypeDescriptor.GetConverter(typeof(int));
            TypeConverter EnumConverter = TypeDescriptor.GetConverter(typeof(LongitudeDirection));
    
            // convert to a string and return
            return IntConverter.ConvertToString(context, culture, LongValue.Degrees) +
                    EnumConverter.ConvertToString(context, culture, LongValue.Direction).Substring(0, 1) +
                    IntConverter.ConvertToString(context, culture, LongValue.Minutes) + MinutesUnit +
                    IntConverter.ConvertToString(context, culture, LongValue.Seconds) + SecondsUnit;
        }
    
        // convert to a instance descriptor
        if (destinationType == typeof(InstanceDescriptor))
        { 
            // no value so we return no instance descriptor
            if (value == null)
                return null;
    
            // strongly typed
            Longitude LongValue = value as Longitude;
    
            // used to descripe the constructor
            MemberInfo Member = null;
            object[] Arguments = null;
    
            // get the constructor for the type
            Member = typeof(Longitude).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(LongitudeDirection) });
    
            // create the arguments to pass along
            Arguments = new object[] { LongValue.Degrees, LongValue.Minutes, LongValue.Seconds, LongValue.Direction };
    
            // return the instance descriptor
            if (Member != null)
                return new InstanceDescriptor(Member, Arguments);
            else
                return null;
        }
    
        // call the base converter
        return base.ConvertTo(context, culture, value, destinationType);
    }
    
    
    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        // no value so we return a new Longitude instance
        if (value == null)
            return new Longitude();
    
        // convert from a string
        if (value is string)
        {
            // get strongly typed value
            string StringValue = value as string;
    
            // empty string so we return a new Longitude instance
            if (StringValue.Length <= 0)
                return new Longitude();
    
            // get the position of the West longitude separator
            int DirectionPos = StringValue.IndexOf(LongitudeDirection.West.ToString().Substring(0, 1));
            LongitudeDirection Direction = LongitudeDirection.West;
    
            // if not found get the position of the East longitude separator
            if (DirectionPos == -1)
            {
                DirectionPos = StringValue.IndexOf(LongitudeDirection.East.ToString().Substring(0, 1));
                Direction = LongitudeDirection.East;
            }
    
            // get the minutes and seconds characters
            int MinutesPos = StringValue.IndexOf(MinutesUnit);
            int SecondsPos = StringValue.IndexOf(SecondsUnit);
    
            // no minutes present
            if (MinutesPos == -1)
                throw new Exception(MinutesMissing);
    
            // no seconds present
            if (SecondsPos == -1)
                throw new Exception(SecondsMissing);
    
            // no minutes present
            if (DirectionPos == -1)
                throw new Exception(DirectionMissing);
    
            // no degrees present
            if (DirectionPos == 0)
                throw new Exception(DegreesMissing);
    
            // get the type converters we need
            TypeConverter IntConverter = TypeDescriptor.GetConverter(typeof(int));
    
            // get the degrees, minutes and seconds value
            int Degrees = (int)IntConverter.ConvertFromString(context, culture, StringValue.Substring(0, DirectionPos));
            int Minutes = (int)IntConverter.ConvertFromString(context, culture, StringValue.Substring(DirectionPos + 1, MinutesPos - DirectionPos - 1));
            int Seconds = (int)IntConverter.ConvertFromString(context, culture, StringValue.Substring(MinutesPos + 1, SecondsPos - MinutesPos - 1));
    
            // create a new Longitude instance with these values and return it
            return new Longitude(Degrees, Minutes, Seconds, Direction);
        }
    
        // otherwise call the base converter
        else
            return base.ConvertFrom(context, culture, value);
    }

    有了这个转换类LongitudeTypeConverter,该怎么使用呢。其实很简单就是使用我们上面实现的四个方法,

     
    class Test
    {
        public static void Main(string[] args)
        {
            //将Longitude类转换到string类型
            Longitude longitude = new Longitude(10, 11, 12, LongitudeDirection.East);
            LongitudeTypeConverter converter = new LongitudeTypeConverter();
    
            string strLongitude = "";
            if (converter.CanConvertTo(typeof(string)))
            {
                strLongitude = (string)converter.ConvertTo(longitude, typeof(string));
            }
            System.Console.WriteLine(strLongitude);
    
            //将string还原回Longitude类
            Longitude longitude1 = new Longitude();
            if (converter.CanConvertFrom(typeof(string)))
            {
                longitude1 = (Longitude)converter.ConvertFrom(strLongitude);
            }
            System.Console.WriteLine(longitude1.Degrees);
            System.Console.WriteLine(longitude1.Direction);
            System.Console.WriteLine(longitude1.Minutes);
            System.Console.WriteLine(longitude1.Seconds);
        }
    }

    输出结果是
    10E11'12''
    10
    East
    11
    12
    从结果中我们可以看到实现了我们预期的效果。
    这些在一般代码里面可以用到,但从转换的结果中我们可以联想,web页面设计的两种模式(设计模式,源代码模式),在源代码模式我们显示的是string,但在设计模式我们显示控件的外观,这里就关系到TypeConverter类了,当然还有TypeConverterAtrribute。
    下面就要说下这个TypeConverterAtrribute了。

  • 相关阅读:
    day13_迭代器,生成器
    作业礼包
    day12_装饰器进阶
    装饰器作业
    day11_装饰器
    函数作业
    day10-函数进阶
    数据类型-习题解答
    day09_函数
    PHP 完美分页
  • 原文地址:https://www.cnblogs.com/jshchg/p/12125101.html
Copyright © 2020-2023  润新知