高级数据处理

运算符重载

大多数情况下,我们并不需要自定义运算符的运算规则,但在C#中,我们的确可以这么做,通过operator关键字,我们就可以定义运算符的规则,如下面的代码。

C#
using System.Collections.Generic;
public class C11<T> : List<T>{
    public static C11<T> operator +(C11<T> obj1, C11<T> obj2)
    {
        C11<T> result = new C11<T>();
        if (obj1 != null) result.AddRange(obj1);
        if (obj2 != null) result.AddRange(obj2);
        return result;
    }
}

本例中的C11类继承于List<T>泛型类,其中定义了列表的+运算符;请注意运算符的定义,必须使用public、static和operator关键字,一对圆括号中定义的参数就是运算数,有一个。一般来讲,运算符的运算结果和运算数是相同类型。

下面的代码,我们在Main()方法中测试C11类的使用。

C#
using System;
namespace HelloProject
{
    class Program
    {
        static void Main(string[] args)
        {
            C11<string> lst1 = new C11<string>();
            lst1.Add("111");
            lst1.Add("222");
            C11<string> lst2 = new C11<string>();
            lst2.Add("aaa");
            lst2.Add("bbb");
            //
            C11<string> lst3 = lst1 + lst2;
            for (int i = 0; i < lst3.Count; i++)
                Console.WriteLine(lst3[i]);
        }
    }
}

代码执行结果如下图。

对象复制

前面的课程中,我们了解到,引用类型的默认传递方式是传递引用,如果需要完成复制一个对象,最直接的方法就是使用序列化,但前提是对象的类型必须声明SerializableAttribute特性(System命名空间)。

下面的代码,我们定义了C12类,其中就使用了SerializableAttribute特性,请注意,声明特性时,可以省略其中的Attribute(特性)。

C#
using System;
[Serializable]
public class C12
{
    public string Name { get; set; }
    public uint Age { get; set; }
}

下面的代码,我们先来回顾一下对象的默认传递方式。

C#
using System;
namespace HelloProject
{
    class Program
    {
        static void Main(string[] args)
        {
            C12 obj1 = new C12() { Name = "Tom", Age = 30 };
            C12 obj2 = obj1;
            Console.WriteLine("obj1,Name='{0}',Age='{1}'", obj1.Name, obj1.Age);
            Console.WriteLine("obj2,Name='{0}',Age='{1}'", obj2.Name, obj2.Age);
            Console.WriteLine();
            obj2.Name = "Jerry";
            obj2.Age = 33;
            Console.WriteLine("obj1,Name='{0}',Age='{1}'", obj1.Name, obj1.Age);
            Console.WriteLine("obj2,Name='{0}',Age='{1}'", obj2.Name, obj2.Age);
        }
    }
}

代码执行结果如下图,我们可以看到,默认情况下对象是按引用传递,obj1和obj2对象实际指向的是同一个对象体。

可序列化的对象在复制时,还需要一些中间过程,如下面的代码,我们在CObj类中封装相关代码。

C#
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
public class CObj
{
    // 将对象序列化成字节数组
    public static byte[] ToByteArray(object obj)
    {
        if (obj == null) return null;
        using (MemoryStream s = new MemoryStream())
        {
            IFormatter f = new BinaryFormatter();
            f.Serialize(s, obj);
            return s.GetBuffer();
        }
    }
    // 将字节数组反序列化成对象
    public static object FromByteArray(byte[] Bytes)
    {
        using (MemoryStream s = new MemoryStream(Bytes))
        {
            IFormatter f = new BinaryFormatter();
            return f.Deserialize(s);
        }
    }
    // 克隆可序列化对象
    public static object Clone(object obj)
    {
        if (obj == null) return null;
        byte[] arrByte = ToByteArray(obj);
        return FromByteArray(arrByte);
    }
}

代码中共定义了三个方法,分别是:

  • ToByteArray()方法,将对象序列化为字节数组。
  • FromByteArray()方法,将字节数组还原成对象(反序列化)。
  • Clone()方法,复制一个对象。

使用Clone()方法复制对象是,返回的是object类型,还需要as运算符进行转换成相应的目标类型,如下面的代码。

C#
using System;
namespace HelloProject
{
    class Program
    {
        static void Main(string[] args)
        {
            C12 obj1 = new C12() { Name = "Tom", Age = 30 };
            C12 obj2 = CObj.Clone(obj1) as C12;
            Console.WriteLine("obj1,Name='{0}',Age='{1}'", obj1.Name, obj1.Age);
            Console.WriteLine("obj2,Name='{0}',Age='{1}'", obj2.Name, obj2.Age);
            Console.WriteLine();
            obj2.Name = "Jerry";
            obj2.Age = 33;
            Console.WriteLine("obj1,Name='{0}',Age='{1}'", obj1.Name, obj1.Age);
            Console.WriteLine("obj2,Name='{0}',Age='{1}'", obj2.Name, obj2.Age);
        }
    }
}

代码执行结果如下图。

Type类

Type类可以帮助我们更灵活地处理数据类型,获取类型的Type对象时,可以参考如下代码。

C#
using System;
namespace HelloProject
{
    class Program
    {
        static void Main(string[] args)
        {
            Type t1 = Type.GetType("System.Int32");
            Type t2 = typeof(int);
            Type t3 = typeof(System.Int32);
            Console.WriteLine(t1.FullName);
            Console.WriteLine(t2.FullName);
            Console.WriteLine(t3.FullName);
        }
    }
}

代码执行结果如下图。

获取类型的基本信息时,可以使用如下属性:

  • Name属性,类型的基本名称,如Int32。
  • FullName属性,包含命名空间和类型名称的完成名称,如System.Int32。
  • Namesapce属性,类型定义的命名空间,如System。
  • IsValueType属性,是否为值类型,如结构或枚举类型。
  • IsClassType属性,是否为引用类型,如类或委托类型。
  • BaseType属性,返回类型的基类类型(Type),Object类型的基类为null。
  • IsAbstract属性,是否为抽象类。
  • IsArray属性,是否为数组。
  • IsGenericType属性,是否为泛型类型。
  • IsInterface属性,是否为接口类型。
  • IsNested属性,是否为嵌套类型。
  • IsSerializable属性,类型是否可以序列化。
  • IsEnum属性,是否为枚举类型。

Type类型中的常用方法有:

  • IsSubClassOf()方法,判断类型是否为另一个类型的子类。
  • GetMethod()方法,根据方法名返回方法信息(System.Reflection.MethodInfo类型),如果方法不存在返回null值;此方法可以判断类型中是否定义了指定的方法。
  • GetProperty()方法,根据属性名返回属性信息(System.Relection.ProperytInfo类型),如果属性不存在返回null值。此外,GetProperties()方法可以返回所有属性,返回类型为PropertyInfo[]。
  • GetField()方法,根据字段名返回字段信息(System.Reflection.FieldInfo类型)。
  • GetEvent()方法,根据事件名返回事件信息(System.Reflection.EventInfo类型)。
  • GetInterfaces()方法,返回类型实现的接口数组,返回类型为Type[]。

接下来,我们从Color类中提取所有的命名颜色属性。Color类定义在System.Drawing命名空间,如果项目中还没有引用此命名空间,可以通过菜单“项目”>>“添加引用”或“解决方案资源管理器”中的“引用”右键菜单“添加引用”添加,如下图。

下面的代码就是从Color类中提到所有命名颜色属性(Color类型,不包含透明色)。

C#
using System;
using System.Drawing;
using System.Reflection;
namespace HelloProject
{
    class Program
    {
        static void Main(string[] args)
        {
            Type colorType = typeof(Color);
            PropertyInfo[] pi = colorType.GetProperties();
            for(int i=0;i<pi.Length;i++)
            {
                if (pi[i].PropertyType == colorType && pi[i].Name != "Transparent")
                    Console.WriteLine(pi[i].Name);
            }
        }
    }
}
本站内容均为原创作品,转载请注明出处,本页面网址为:http://caohuayu.com/chy/article/Article.aspx?code=cc002016