zoukankan      html  css  js  c++  java
  • More Effective C# Item6 : 使用委托定义类型参数上的方法约束

        有时,我们看C#泛型中的约束机制,可能认为会过于简单,因为约束只能制定一个基类、接口、类或者结构和一个无参的构造函数,还有其他很多无法实现,例如无法设置约束,使其必须实现某些方法,或者必须满足某种重载形式的构造函数。

        我们可以考虑下面的问题,如何让泛型类型必须支持Add()方法。有以下两种方式,分别进行讨论。

        第一种方式,按部就班,首先定义一个泛型接口,声明Add()方法,然后让泛型类型实现该接口,代码如下。

    1 public interface IAddHandler<T>
    2 {
    3 T Add(T left, T right);
    4 }
    5
    6  public class AddHandlerForInt : IAddHandler<int>
    7 {
    8 public int Add(int left, int right)
    9 {
    10 return left + right;
    11 }
    12 }

        这种方式,对于使用者来说,是很不方便的,因为它为了支持Add()方法, 必须要实现一个泛型的接口,很容易会使得系统变得过于复杂。

        接下来,我们看第二种方案,我们可以指定一个匹配泛型类将要调用的委托的签名,这并不会给泛型类的开发人员带来任何额外的工作,反而却能够为泛型类型的使用者节省大量的时间。

        来看下面的代码。

    1 public class FuncTest
    2 {
    3 public static T Add<T>(T left, T right, System.Func<T, T, T> addFunc)
    4 {
    5 return addFunc(left, right);
    6 }
    7 }

        下面是如何使用这个泛型类型。

    1 public static void AddTest()
    2 {
    3 Console.Write("1 + 1 = ");
    4 Console.WriteLine(FuncTest.Add<int>(1, 1, (x, y) => x + y).ToString());
    5 }

        程序运行结果很简单,就不赘述了。

        下面我们来看一个比较复杂的例子,假设有这样的场景,我们现在需要显示很多职员的姓名和年龄,但是这些姓名和年龄是分别放置在两个集合中,如何将这两个集合中的数据,恢复成完整的职员信息。

        来看下面的代码。

    代码
    1 public class FuncTest
    2 {
    3 public static T Add<T>(T left, T right, System.Func<T, T, T> addFunc)
    4 {
    5 return addFunc(left, right);
    6 }
    7
    8 public static IEnumerable<TOutput> Merge<T1, T2, TOutput>(IEnumerable<T1> left, IEnumerable<T2> right, System.Func<T1, T2, TOutput> generator)
    9 {
    10 IEnumerator<T1> enumLeft = left.GetEnumerator();
    11 IEnumerator<T2> enumRight = right.GetEnumerator();
    12 while (enumLeft.MoveNext() && enumRight.MoveNext())
    13 {
    14 yield return generator(enumLeft.Current, enumRight.Current);
    15 }
    16 }
    17 }
    18
    19  public class Employee
    20 {
    21 private string m_strName = string.Empty;
    22 public string Name
    23 {
    24 get { return m_strName; }
    25 set { m_strName = value; }
    26 }
    27
    28 private int m_nAge = 0;
    29 public int Age
    30 {
    31 get { return m_nAge; }
    32 set { m_nAge = value; }
    33 }
    34
    35 public Employee(string name, int age)
    36 {
    37 m_strName = name;
    38 m_nAge = age;
    39 }
    40
    41 public override string ToString()
    42 {
    43 return string.Format("Name : {0}, Age : {1}", m_strName, m_nAge);
    44 }
    45 }

        下面是测试代码。

    1 public static void MergeTest()
    2 {
    3 string[] arrName = new string[]{ "Wing", "Unknown"};
    4 int[] arrAge = new int[] { 25, 30 };
    5 IEnumerable<Employee> enumEmployee = FuncTest.Merge<string, int, Employee>(arrName, arrAge, (name, age) => new Employee(name, age));
    6 IEnumerator<Employee> enumerator = enumEmployee.GetEnumerator();
    7 while (enumerator.MoveNext())
    8 {
    9 Console.WriteLine(enumerator.Current.ToString());
    10 }
    11 }

        上述代码的执行结果,也是很简单的。

        上述两个实例中使用到了两个在.NET框架中提供的委托,声明如下。

    1 delegate Func<T1, T2, TOutput>();
    2  delegate TOutput Func<T1, T2, TOutput>(T1 arg1, T2 arg2);

        当然,在实际项目过程中,我们也可以根据项目的具体需求,定义对应的泛型代理。

        通常,最好的设计是使用类约束或者接口约束来指定需要的约束,.NET基础类库中有很多现成的例子,但是,如果你需要仅为某个特定的泛型方法或者类创建自定义的接口契约,那么也可以使用委托将该契约声明成方法的约束,这将会让类型的使用者更加方便,同时泛型类型将更易于使用,易于理解。不要让任何无法由基本约束直接支持的语义上的约束限制了你的设计。

        
    作者:李潘
             
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    [刷题] 1023 组个最小数 (20分)
    [刷题] 1018 锤子剪刀布 (20分)
    Leetcode 542 01矩阵 图的按距离遍历
    Leetcode 515 每个树行中的最大值 BFS
    Leetcode17.12 BiNode 中序遍历
    Leetcode 513 树左下角的值 DFS 与 BFS
    leetcode 787 K 站中最便宜的航班 DP
    数据可视化基础专题(43):NUMPY基础(8)切片和索引,高级索引(二) 高级索引
    数据可视化基础专题(42):NUMPY基础(7)切片和索引,高级索引(一)
    数据可视化基础专题(41):NUMPY基础(6)数组创建(3) 从数值范围创建数组
  • 原文地址:https://www.cnblogs.com/wing011203/p/1750429.html
Copyright © 2011-2022 走看看