More Effective C# Item6 : 使用委托定义类型参数上的方法约束
2010-06-03 01:01
1006 查看
有时,我们看C#泛型中的约束机制,可能认为会过于简单,因为约束只能制定一个基类、接口、类或者结构和一个无参的构造函数,还有其他很多无法实现,例如无法设置约束,使其必须实现某些方法,或者必须满足某种重载形式的构造函数。
我们可以考虑下面的问题,如何让泛型类型必须支持Add()方法。有以下两种方式,分别进行讨论。
第一种方式,按部就班,首先定义一个泛型接口,声明Add()方法,然后让泛型类型实现该接口,代码如下。
这种方式,对于使用者来说,是很不方便的,因为它为了支持Add()方法, 必须要实现一个泛型的接口,很容易会使得系统变得过于复杂。
接下来,我们看第二种方案,我们可以指定一个匹配泛型类将要调用的委托的签名,这并不会给泛型类的开发人员带来任何额外的工作,反而却能够为泛型类型的使用者节省大量的时间。
来看下面的代码。
下面是如何使用这个泛型类型。
程序运行结果很简单,就不赘述了。
下面我们来看一个比较复杂的例子,假设有这样的场景,我们现在需要显示很多职员的姓名和年龄,但是这些姓名和年龄是分别放置在两个集合中,如何将这两个集合中的数据,恢复成完整的职员信息。
来看下面的代码。
代码
下面是测试代码。
上述代码的执行结果,也是很简单的。
上述两个实例中使用到了两个在.NET框架中提供的委托,声明如下。
当然,在实际项目过程中,我们也可以根据项目的具体需求,定义对应的泛型代理。
通常,最好的设计是使用类约束或者接口约束来指定需要的约束,.NET基础类库中有很多现成的例子,但是,如果你需要仅为某个特定的泛型方法或者类创建自定义的接口契约,那么也可以使用委托将该契约声明成方法的约束,这将会让类型的使用者更加方便,同时泛型类型将更易于使用,易于理解。不要让任何无法由基本约束直接支持的语义上的约束限制了你的设计。
我们可以考虑下面的问题,如何让泛型类型必须支持Add()方法。有以下两种方式,分别进行讨论。
第一种方式,按部就班,首先定义一个泛型接口,声明Add()方法,然后让泛型类型实现该接口,代码如下。
public interface IAddHandler<T> { T Add(T left, T right); } public class AddHandlerForInt : IAddHandler<int> { public int Add(int left, int right) { return left + right; } }
这种方式,对于使用者来说,是很不方便的,因为它为了支持Add()方法, 必须要实现一个泛型的接口,很容易会使得系统变得过于复杂。
接下来,我们看第二种方案,我们可以指定一个匹配泛型类将要调用的委托的签名,这并不会给泛型类的开发人员带来任何额外的工作,反而却能够为泛型类型的使用者节省大量的时间。
来看下面的代码。
public class FuncTest { public static T Add<T>(T left, T right, System.Func<T, T, T> addFunc) { return addFunc(left, right); } }
下面是如何使用这个泛型类型。
public static void AddTest() { Console.Write("1 + 1 = "); Console.WriteLine(FuncTest.Add<int>(1, 1, (x, y) => x + y).ToString()); }
程序运行结果很简单,就不赘述了。
下面我们来看一个比较复杂的例子,假设有这样的场景,我们现在需要显示很多职员的姓名和年龄,但是这些姓名和年龄是分别放置在两个集合中,如何将这两个集合中的数据,恢复成完整的职员信息。
来看下面的代码。
代码
public class FuncTest { public static T Add<T>(T left, T right, System.Func<T, T, T> addFunc) { return addFunc(left, right); } public static IEnumerable<TOutput> Merge<T1, T2, TOutput>(IEnumerable<T1> left, IEnumerable<T2> right, System.Func<T1, T2, TOutput> generator) { IEnumerator<T1> enumLeft = left.GetEnumerator(); IEnumerator<T2> enumRight = right.GetEnumerator(); while (enumLeft.MoveNext() && enumRight.MoveNext()) { yield return generator(enumLeft.Current, enumRight.Current); } } } public class Employee { private string m_strName = string.Empty; public string Name { get { return m_strName; } set { m_strName = value; } } private int m_nAge = 0; public int Age { get { return m_nAge; } set { m_nAge = value; } } public Employee(string name, int age) { m_strName = name; m_nAge = age; } public override string ToString() { return string.Format("Name : {0}, Age : {1}", m_strName, m_nAge); } }
下面是测试代码。
public static void MergeTest() { string[] arrName = new string[]{ "Wing", "Unknown"}; int[] arrAge = new int[] { 25, 30 }; IEnumerable<Employee> enumEmployee = FuncTest.Merge<string, int, Employee>(arrName, arrAge, (name, age) => new Employee(name, age)); IEnumerator<Employee> enumerator = enumEmployee.GetEnumerator(); while (enumerator.MoveNext()) { Console.WriteLine(enumerator.Current.ToString()); } }
上述代码的执行结果,也是很简单的。
上述两个实例中使用到了两个在.NET框架中提供的委托,声明如下。
delegate Func<T1, T2, TOutput>(); delegate TOutput Func<T1, T2, TOutput>(T1 arg1, T2 arg2);
当然,在实际项目过程中,我们也可以根据项目的具体需求,定义对应的泛型代理。
通常,最好的设计是使用类约束或者接口约束来指定需要的约束,.NET基础类库中有很多现成的例子,但是,如果你需要仅为某个特定的泛型方法或者类创建自定义的接口契约,那么也可以使用委托将该契约声明成方法的约束,这将会让类型的使用者更加方便,同时泛型类型将更易于使用,易于理解。不要让任何无法由基本约束直接支持的语义上的约束限制了你的设计。
相关文章推荐
- More Effective C# Item8 :尽可能使用泛型方法,除非需要将类型参数用于实例的字段中
- More Effective C# Item3 : 运行时检查泛型参数的类型并提供特定的算法
- More Effective C# Item2 : 恰到好处的定义约束
- C#定义泛型方法错误-类型“T”必须是引用类型才能用作泛型类型或方法“System.Data.Linq.Table<TEntity>”中的参数“TEntity”
- 《More Effective C++》 Item M2:尽量使用C++风格的类型转换
- \t\t在MSSQL中定义和使用C#自定义类型 SQL Server08表类型参数传递
- C#委托类型-(基本使用方法)
- Effective C# Item6:明辨值类型和引用类型的使用场合
- Effective Java Item2:当构造方法的参数(尤其是可选参数)比较多时使用Builder模式
- More Effective C# Item1 : 使用1.x框架API的泛型版本
- Effective JavaScript Item 21 使用apply方法调用函数以传入可变参数列表
- 【C#】delegate委托的定义和使用方法
- more effective c++笔记1-----Item M2:尽 量使用C++风格的类型转换
- C#_delegate - Pair<T> & 简单顺序逆序 & 方法委托(在Pair类下)&枚举类型 混搭使用
- C# 001 --- 正确的删除一个控件的事件函数 --- 解决错误: “A”不包含“B”的定义,并且找不到可接受类型为“A”的第一个参数的扩展方法“B”(是否缺少 using 指令或程序集引用?)
- C#方法参数传递-同时使用ref和out关键字
- C# 将方法作为参数的方法 -- 委托
- C#反射得并调用方法(方法参数类型)
- Effective Java Item3:使用私有构造方法或者枚举类型实现单例(转自sunjavaduke)
- More Effective C++ ——02_尽量使用C++风格的类型转换