zoukankan      html  css  js  c++  java
  • C#函数式程序设计之泛型(下)

    C#函数式程序设计之约束类型

    每当使用泛型类型时,可以通过where字句对泛型添加约束:

    static void OutputValue<T>(T value) where T : ListItem<string>
    {
          Console.WriteLine("String list value: {0}", value.Value);
    }
    

    这个例子直观地声明了一个约束:类型T必须与ListItem<string>相匹配。泛型类型约束T:X表示T可以是X、X的派生对象或X的实现(假如X是一个接口)。换言之,假如类型T的一个实例为t,则可以把它赋给一个变量:X x=t;

    约束可以使用具体的类型,但是在这些情形下,类型不可以是密封的。有几个特殊的关键字可以取代或补充类型声明符。关键字class表示此类型必须是一个引用类型,而struct表示它必须是一个值类型。当new()与class或者任何具体类型一起使用时,可以给这个类型定义一个默认的构造函数。

    约束的最后一个应用是定义两个类型参数的关系。例如,对于类型参数的T和U,约束T:U表示T必须与U相容。

    使用约束时,有一点必须记住:泛型的基本作用是提供一个类型安全的方法,使代码可以处理不同类型的数据。约束用得越多,则离这个思想越远,因为约束降低了灵活性。

    C#函数式程序设计之其他泛型类型

    除了方法与类外,结构体、委托和接口也可以使用类型参数。结构体和接口使用类型参数是显而易见的,其用法与类相似:

    public struct MyPoint<T> where T : struct
    {
          public MyPoint(T x, T y)
          {
                this.x = x;
                this.y = y;
          }
    
          private readonly T x;
          public T X
          {
               get
               {
                    return x;
               }
          }
    
          private readonly T y;
          public T Y
          {
               get
               {
                    return y;
               }
          }
    
          public interface IListItem<T>
          {
               T Value { get; }
                ListItem<T> Prepend(T value);
           }
    }
    

    即使是委托,其用法也丝毫没有令人吃惊的地方:

    public delegate R CreateDelegate<T, R>(T param);
    public class ParameterFactory<T, R>
    {
          CreateDelegate<T, R> createDelegate;
          public ParameterFactory(CreateDelegate<T, R> createDelegate)
          {
               this.createDelegate = createDelegate;
          }
    }
    

    使用了泛型后,这些委托几乎可以代表任何函数。

    C#函数式程序设计之协变与逆变

    如果一个操作保留了类型原来的顺序,则成为协变,如果颠倒它们的顺序,则称为逆变。所谓的类型顺序是指:通用类型的顺序值比专用类型的顺序值强。

    下面这个例子说明C#支持协变,首先定义一个对象数组:

    object[] objects = new object[3];
    objects[0] = new object();
    objects[1]="Just a string";
    objects[2]=10;
    

    可以把不同的值插入到这个数组中,因为所有数据最终都是派生自.NET中的Object类型。换言之,Object是一个非常通用的类型,即它是一个强类型。接下来说明.NET支持协变,它把一个弱类型的值赋给强类型的变量:

    string[] stringsTest = new string[] { "one", "two", "three" };
    objects = stringsTest;
    

    变量objects属于object[]类型,它可以保存实际类型为string[]的值。仔细想想,我们希望如此,但是结果不是这样的,毕竟,虽然string派生自object,但是string[]并不是派生自object[]。尽管如此,由于本例中C#支持协变,这个赋值是可行的。

    说明逆变思想需要一个比较复杂的例子:

    public class Person:IPerson
    {
        public Person() { }
    }
    
    public class Woman : Person
    {
        public Woman() { }
    }
    

    Woman是从Person派生出来的类,现在分析如下两个函数:

    static void WorkWithPerson(Person person)
    { }
    
    static void WorkWithWonman(Woman woman)
    { }
    

    其中一个函数作用于Woman类,另一个函数比较通用,作用于Person类。从Woman类可以定义以下两个委托和函数:

    delegate void AcceptWomanDelegate(Woman person);
    
    static void DoWork(Woman woman, AcceptWomanDelegate acceptWoman)
    {
         acceptWoman(woman);
    }
    

    DoWork函数接受一个Woman参数和一个函数引用,后者也接受一个Woman参数。DoWork函数把Woman实例传递给委托。元素类型大小为:Person比Woman强,WorkWithPerson比WorkWithWoman强,为了应用逆变,在此认为WorkWithPerson比AcceptWomanDelegate强,看以下三行代码:

    Woman woman = new Woman();
    DoWork(woman, WorkWithWonman);
    DoWork(woman, WorkWithPerson);
    

    首先创建一个Woman实例,然后调用DoWork函数,把Woman实例和WorkWithWoman方法的引用地址传递给DoWork。后者显然是与委托类型AcceptWomanDelegate相容——两者都只有一个Woman类型参数,没有返回值。但第三行代码有点怪,根据AcceptWomanDelegate的要求,WorkWithPerson方法接受一个Person参数,而不是一个Woman参数。虽然如此,WorkWithPerson还是与委托类型相容,这是逆变的缘故。

    因此,在委托类型下,强类型可以保存在弱类型的变量中。

    变异也能应用在泛型中。如下代码:

    List<object> objectList = new List<object>();
    List<string> stringList = new List<string>();
    objectList = stringList;
    

    以上代码并没有得到C#的支持,编译器会报如下错误:

    在C#和.NET4.0中,泛型的变异支持已删除,现在要使用泛型类型参数,可以用新增的关键字 in 和 out。这两个关键字定义或限制某个类型参数的数据流动方向,允许变异发生。

    作者:Ribbon 出处: http://www.cnblogs.com/Ribbon/ 本文版权归作者和博客园共有,欢迎转载。未经作者同意下,必须在文章页面明显标出原文链接及作者,否则保留追究法律责任的权利。 如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!
  • 相关阅读:
    SpringCloud微服务基础学习
    EF6 + MySql 建立项目引用失败
    Forword(请求转发)与Redirect(重定向)区别
    Java 中 Hashtable与HashMap的区别
    cookie和session
    configparser模块的简单使用
    列表中的陷阱
    Python3面向对象编程总结
    Python---RabbitMQ的使用
    Django的template自定义函数的创建和使用
  • 原文地址:https://www.cnblogs.com/Ribbon/p/3604339.html
Copyright © 2011-2022 走看看