深度解析 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类型

CanConverterFrom
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            else
                return base.CanConvertFrom(context, sourceType);
        }
CanConverterTo
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if ((destinationType == typeof(string)) |
                (destinationType == typeof(InstanceDescriptor)))
                return true;
            else
                return base.CanConvertTo(context, destinationType);
        }
ConvertTo
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);
        }
ConvertFrom
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了。

待续...

参考:http://www.codeproject.com/KB/webforms/TypeConverters.aspx

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏landv

excel_VB宏脚本_批量生成点餐宝接受的格式

20120
来自专栏菩提树下的杨过

Silverlight与WPF中BeginInvoke的差异

Silverlight/WPF中,如果要在多线程中对界面控件值做修改,用Dispatcher对象的BeginInvoke方法无疑是最方便的办法 ,见:温故而知新...

23280
来自专栏菩提树下的杨过

java学习:调用 java web service

先写一个java的class:AwbModel(相当于要在web service中传输的实体对象) package webservicesample; pub...

27960
来自专栏程序员的SOD蜜

使用反射+缓存+委托,实现一个不同对象之间同名同类型属性值的快速拷贝

最近实践一个DDD项目,在领域层与持久层之间,Domain Model与Entity Model之间有时候需要进行属性值得拷贝,而这些属性,尽管它所在的类名称不...

22790
来自专栏GreenLeaves

C# 字符串操作基本过程(Equals、Compare、EndsWith等处理方法)

41320
来自专栏程序你好

理解C#语言中相等Equality 和唯一 Identity

10520
来自专栏landv

从零开始学习C#——HelloWorld(一)

14750
来自专栏技术博客

编写高质量代码改善C#程序的157个建议[10-12]

  本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:

10040
来自专栏Java后端技术

22中编程语言的HelloWorld

8010
来自专栏技术博客

Dynamic 动态类型 和双问号??的使用

1.dynamic关键字用于声明一个动态对象,然后通过该动态对象去调用方法或读写属性。以前我们都是在运行时通过反射,Emit,CodeDom等技术来完成。创建一...

22320

扫码关注云+社区

领取腾讯云代金券