扩展方法和分部方法

一、概述
     从概念上讲,扩展方法就是装饰模式(Decorator,一种结构型模式)的一种实现,装饰模式实际上就是利用集成和复合动态添加行为。
    当不能给现有类添加行为时,就可以考虑扩展方法,其作用就相当于这个方法属于这个类一样。例如,当我们不能继承一个密封类来为其添加行为时,同样,一些基础类型也是无法继承的。显然,扩展方法提供了一种无需集成的添加行为的方式。
    扩展方法允许定义一个静态方法,并用实例方法的语法来调用。

    我们在使用.net或第三方类库时,根据业务需要需增加一个函数类处理,但又不想在其他类中调用只想在原有类中调用,又不能直接修改源码,该如何实现呢?这时我们可以用扩展方法实现。

二、扩展方法及其使用规则
2.1 扩展方法的用法
        扩展方法可以扩展密封类型,无需继承
        扩展方法可以避免出现失控的深度继承层次体系
        扩展方法是静态类中的继承方法,即必须在非泛型的静态类中声明
        扩展方法第一个参数的类型之前必须使用this修饰符;this指代将要被扩展的类型,且扩展方法至少要有一个参数
        不支持扩展属性、事件和操作符,只支持扩展方法

        相对于实例方法而言,扩展方法的可视性要差一些,而且限制也会多一些
        扩展方法是通过实例语法来调用的
        扩展方法的优先级比常规方法要低;因此,如果某个类拥有一个同名方法的话,那么被调用的将是实例方法
        扩展方法能在字面量上调用
       扩展方法可以用在密封类和内部类型上,因为扩展方法自动支持装箱和拆箱;也就是说,当值类型被当作对象使用时,.NET Framework将会使用一个类将它包装起来
        扩展方法并不是真正的成员,因此只能在其中访问被扩展对象的公共成员
        扩展方法隐式使用ExtensionAttribute;在VB .NET中,ExtensionAttribute是显示使用的。
        编译器在静态类中查找扩展方法时,要求静态类本身必须具有文件作用域;即如果静态类嵌套在另一个类中,编译会报错:扩展方法必须在顶级静态类中定义。
        编译器允许创建委托来引用一个对象上的扩展方法。

【示例】

namespace test1
{
    public static class Test
    {
        关键字this+要扩展对象(可以是.net类库或者第三方类库中的类,自定义类可行) 
        public static int ToInt32(this string str) 
        {
            return Convert.ToInt32(str);
        }
    }
}
namespace test1
{
    class Program
    {
        static void Main(string[] args)
        {
            string str = "200";
            int i = str.ToInt32();
            Console.WriteLine(i);
            Console.ReadKey();
        }
    }
}

 

【示例:扩展方法是静态类中的继承方法】

#region 定义一个不带有返回类型的扩展方法
            var song = new { songer = "huangjiaju", geyu = "haikuotiankong" };
            song.Dump();
            #endregion

 

#region 001
    public static class Dumper
    {
        //this:指代将要被扩展的类型
        public static void Dump(this Object o)
        {
            //初始化 System.Reflection.PropertyInfo 类的新实例。关于对象的属性信息
            //获取当前实例的 System.Type。
            // 返回当前 System.Type 的所有公共属性。
            PropertyInfo[] properties = o.GetType().GetProperties();
            foreach (PropertyInfo p in properties)
            {
                try
                {
                    //将指定字符串中的格式项替换为两个指定对象的字符串表示形式。
                    Console.WriteLine(string.Format("Name:{0},Value:{1}", p.Name, p.GetValue(o, null)));
                }
                catch
                {
                    Console.WriteLine(string.Format("Name:{0},Value:{1}", p.Name, "unk."));
                }
            }
        }
    }
    #endregion

分析:
    示例中定义了一个匿名类型,它含有两个属性。由于Dump作用于object类型,而所有类型又都继承自object,因此,该匿名类型也可以访问到Dump。

 

 

【示例】

#region 定义一个带有返回类型的扩展方法
            var songs = new { songer = "weixiaoqi", gequ = "like you" };
            Console.WriteLine(songs.Dump());
            #endregion


#region 002
    public static class Dumper
    {
        public static string Dump(this Object o)
        {
            PropertyInfo[] properties = o.GetType().GetProperties();
            //表示可变字符字符串
            StringBuilder builder = new StringBuilder();
            foreach (PropertyInfo p in properties)
            {
                try
                {
                    builder.AppendFormat(string.Format("Name:{0},Value:{1}", p.Name, p.GetValue(o, null)));
                }
                catch
                {
                    builder.AppendFormat(string.Format("Name:{0},Value:{1}", p.Name, "unk."));
                }
                //将默认的行终止符追加到当前对象的末尾
                builder.AppendLine();
            }
            return builder.ToString();
        }
    }
    #endregion

 

    扩展方法遵循一个固定的模式。定义一个公共静态类,然后在这个类里面定义一个公共静态方法。该方法的第一个参数必须使用this修饰符。第一个参数(用this修饰的)代指将要被扩展的类。在第一个参数之后,加上其他参数。这些静态方法既可以通过参数来重载,也可以是泛型方法。

【示例:重载扩展方法】

#region 重载扩展方法:第一个所接受的扩展类型是object,并转储单个对象的属性;第二个Dump扩展的是IList,它迭代这个集合中的每一项并对其调用Test  
            var songs = new[]
            {
                new {songer="weixiaoqi",song="like you"},
                new {songer="xiaoxu",song="haikuotiankong"},
                new {songer="qiqi",song="chengdu"},
                new {songer="xiaoxiao",song="hebei"}
            };
            songs.Test();
            #endregion


#region 004
    public static class Tester
    {
        public static void Test(this Object o)
        {
            PropertyInfo[] properties = o.GetType().GetProperties();
            foreach (PropertyInfo p in properties)
            {
                try
                {
                    Console.WriteLine(string.Format("Name:{0},Value:{1}", p.Name, p.GetValue(o, null)));
                }
                catch
                {
                    Console.WriteLine(string.Format("Name:{0},Value:{1}", p.Name, "unk."));
                }
            }
        }
        //object类型是所有类型的基类型,而很多集合类型又都实现了IList,这样通过重载,可以编写
        //不同形式的Test方法,编译器会判断具体该使用哪个
        public static void Test(this IList list)
        {
            foreach (object o in list)
            {
                o.Test();
            }
        }
    }
    #endregion

 

 

2.2 定义泛型扩展方法

using System;
using System.Collections.Generic;
using System.Data.SqlClient;

namespace 定义泛型扩展方法
{
    class Program
    {
        static void Main(string[] args)
        {
            //连接字符串
            string connectionString = "Data Source=.;Initial Catalog=db_Test;Integrated Security=True";
            //创建一个User类型的集合,用来存储每一个User类型的对象
            List<User> users = new List<User>();
            //打开数据库并及时释放资源
            using(SqlConnection con=new SqlConnection(connectionString))
            {
                //编写查询语句:从表中查询所有数据,并作为参数传给命令语句
                var selectSQL = "select * from tb_User";
                SqlCommand com = new SqlCommand(selectSQL, con);
                con.Open();
                //返回结果:System.Data.SqlClient.SqlDataReader 对象。
                SqlDataReader reader = com.ExecuteReader();

                //一条一条地读取数据
                while (reader.Read())
                {
                    User user = new User();
                    user.Read(reader);
                    users.Add(user);
                }
            }
            //use the dumper to send everything to the console  
            users.Dump(Console.Out);
            Console.ReadLine();
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace 定义泛型扩展方法
{
    /// <summary>
    /// 输出查询所得结果
    /// </summary>
    public static class Dumper
    {
        /// <summary>
        /// 对Object定义扩展方法
        /// </summary>
        /// <param name="o">定义扩展的对象</param>
        /// <param name="writer">有序字符编写器对象</param>
        public static void Dump(this Object o,TextWriter writer)
        {
            PropertyInfo[] properties = o.GetType().GetProperties();
            foreach (PropertyInfo p in properties)
            {
                try
                {
                    writer.WriteLine(string.Format("Name:{0},Value:{1}", p.Name, p.GetValue(o, null)));
                }
                catch
                {
                    writer.WriteLine(string.Format("Name:{0},Value:{1}", p.Name, "unk."));
                }
            }
        }
        /// <summary>
        /// 将集合循环输出
        /// </summary>
        /// <typeparam name="T">集合中对象的类型</typeparam>
        /// <param name="list"></param>
        /// <param name="writer"></param>
        public static void Dump<T>(this IList<T> list,TextWriter writer)
        {
            foreach(object o in list)
            {
                o.Dump(writer);
            }
        }
    }
}
using System;
using System.Data;

namespace 定义泛型扩展方法
{
    public static class ReaderHelper
    {
        /// <summary>
        /// 对每条数据进行处理,如果字段为null,设置默认值
        /// </summary>
        /// <param name="user">对User定义泛型扩展方法</param>
        /// <param name="reader">数据读取器对象</param>
        public static void Read(this User user, IDataReader reader)
        {
            user.UserName = user.SafeRead(reader, "UserName", "未注册");
            user.UserPass = user.SafeRead(reader, "UserPass", "000");
            user.UserQPass = user.SafeRead(reader, "UserQPasd", "000");
            user.Sex = user.SafeRead(reader, "Sex", '中');
            user.Age = user.SafeRead(reader, "Age", 0);
        }
        /// <summary>
        /// 判断字段是否为空,如若为空,返回默认值;反之,返回当前值
        /// </summary>
        /// <typeparam name="T">类型,方法的返回值类型</typeparam>
        /// <param name="entity">对EntityClass定义泛型扩展方法</param>
        /// <param name="reader">数据读取器对象</param>
        /// <param name="filedName">字段名</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns></returns>
        public static T SafeRead<T>(this EntityClass entity, IDataReader reader, string filedName, T defaultValue)
        {
            try
            {
                object o = reader[filedName];
                if (o == null || o == DBNull.Value)
                {
                    return defaultValue;
                }
                return (T)Convert.ChangeType(o, defaultValue.GetType());
            }
            catch
            {
                return defaultValue;
            }
        }
    }
}
 
namespace 定义泛型扩展方法
{
    /// <summary>
    /// 实体类
    /// </summary>
    public class User:EntityClass
    {
        /// <summary>
        /// 初始化
        /// </summary>
        public User()
        {

        }
        /// <summary>
        /// 用户名
        /// </summary>
        private string _username;
        public string UserName
        {
            get
            {
                return _username;
            }
            set
            {
                _username = value;
            }
        }
        /// <summary>
        /// 用户密码
        /// </summary>
        private string _userpass;
        public string UserPass
        {
            get
            {
                return _userpass;
            }
            set
            {
                _userpass = value;
            }
        }
        /// <summary>
        /// 确认密码
        /// </summary>
        private string _userqpass;
        public string UserQPass
        {
            get
            {
                return _userqpass;
            }
            set
            {
                _userqpass = value;
            }
        }
        /// <summary>
        /// 性别
        /// </summary>
        private char _sex;
        public char Sex
        {
            get
            {
                return _sex;
            }
            set
            {
                _sex = value;
            }
        }
        /// <summary>
        /// 年龄
        /// </summary>
        private int _age;
        public int Age
        {
            get
            {
                return _age;
            }
            set
            {
                _age = value;
            }
        }
    }
}

 

 

namespace 定义泛型扩展方法
{
    /// <summary>
    /// 实体类
    /// </summary>
    public class EntityClass
    {
    }
}

分析:

      该示例的核心在于如何对空值即值类型进行处理,而后以何种格式进行输出。

 

三、分部方法

      Bill说:“分部方法就是为自动生成的代码而准备的占位符”。    
      自动代码生成所存在的一个典型问题是,当消费者修改了已生成的代码之后,又重新自动生成了这段代码,那么用户对这段代码所做的修改就会被覆盖掉。该问题的解决方案之一是从自动生成的代码上继承一个子类,然后在这个子类中编写自定义代码。而分部方法能能够让生产者只修改标记为partial的方法。如果消费者想要在某处插入一些行为,则需要提供该分部方法的一个实现。如果没有给出实现,则该分部方法将会被忽略。

分部方法的一些基本规则:     

      分部方法要声明在分部类(Partial Class)中 或结构中    
      分部方法要使用Partial     
      分部方法在声明时不能有方法体     
      分部方法只能返回void     
      分部方法可以是静态的,而且可以有参数和参数修饰符(包括ref和params)     
      分部方法是私有的,不过不能在字面上使用访问修饰符     
      编译器将忽略未使用的分部方法
      分部方法的声明和实现必须具有一致的签名
      如果没有对应的实现部分,便不能在代码中创建一个委托来引用这个分部方法。

【示例】

 
using System;

namespace 定义分部方法
{
    class Program
    {
        static void Main(string[] args)
        {
            CustomerList customers = new CustomerList
            {
                new Customer {CustomerID=1,Name="xushuai" },
                new Customer { CustomerID=2,Name="wangfeng"},
                new Customer { CustomerID=3,Name="xianggou"}
            };
            customers.Dump();
            Console.ReadKey();
        }
    }
}

 

 

using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
using System.Reflection;
using System.Diagnostics;

namespace 定义分部方法
{
    public partial class CustomerList
    {
        private string propertyName;
        private SortDirection direction;
        /// <summary>
        /// 分部方法实现代码,分部方法是私有的,不过不能在字面上使用访问修饰符,即不能显示地使用private
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="direction"></param>
        partial void SpecialSort(string propertyName,SortDirection direction)
        {
            this.propertyName = propertyName;
            this.direction = direction;
            Sort(Comparer);
        }

        private int Comparer(Customer x,Customer y)
        {
            try
            {
                PropertyInfo lhs = x.GetType().GetProperty(propertyName);
                PropertyInfo rhs = y.GetType().GetProperty(propertyName);

                int directionChanger = direction == SortDirection.Ascending ? 1 : -1;

                object o1 = lhs.GetValue(x, null);
                object o2 = rhs.GetValue(y, null);

                //定义一种特定于类型的通用比较方法,值类型或类通过实现此方法对其实例进行排序。
                if (o1 is IComparable&&o2 is IComparable)
                {
                    return ((IComparable)o1).CompareTo(o2) * directionChanger;
                }

                //no sort
                return 0;
            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex.Message);
                return 0;
            }
        }
    }
    /// <summary>
    /// 可枚举类
    /// </summary>
    public partial class CustomerList : List<Customer>
    {
        /// <summary>
        /// 分部方法在声明时不能有方法体
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="direction"></param>
        partial void SpecialSort(string propertyName, SortDirection direction);

        public void Dump()
        {
            SpecialSort("CustomerID", SortDirection.Descending);

            foreach(var customer in this)
            {
                Console.WriteLine(customer.ToString());
            }
        }
    }
    /// <summary>
    /// 实体类
    /// </summary>
    public class Customer
    {
        private int customerID;
        public int CustomerID
        {
            get
            {
                return customerID;
            }
            set
            {
                customerID = value;
            }
        }
        private string name;
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }
        public override string ToString()
        {
            return string.Format("CustomerID={0}, Name={1}", customerID, Name);
        }
    }
}

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值