(10)泛型

*泛型类型概述

参数化类型:

*可空类型也是泛型。

            //System.Nullable<int> nullableInt;
            int? op1 = 5;
            //int result = op1 * 5;无法将int?隐式转换为int,需要显示转换。
            //如果op1是null,就会生成System.InvalidOperationException类型的异常。
            int result = (int)op1 * 5;
            Console.Write(result);
            Console.ReadKey();
            //除了bool?之外的简单可控类型,该造作的结果都是null.对于bool?为& 和|操作会得到非空值。
            //op1    op2     &      |
            //true   null    null   true
            //false  null    false  null
            // null  null    null   null 

利用泛型,可以在声明变量时创建用来处理特定类型的特殊数据结构。使特定泛型类型的每个变量都有相同的内部算法,但数据类型和方法签名可以不同。

用<>来表示要处理的数据类型。

*System.Collection.Generic名称空间中包含的类型有:

List<T>  :T类型对象的集合

Dictionary<K,V>   :与K类型的键值相关的V类型的项的集合

*泛型接口和struct

    //泛型接口
    interface Ipair<T>
    {
        T first { set; get; }
        T second { set; get; }
    }

    //实现一个泛型接口
    public struct Pair<T> : Ipair<T>
    {
        public T first
        {
            get
            {
                return _First;
            }
            set 
            {
                _First = value; 
            }
        }
        private T _First;

        public T second
        {
            get
            {
                return _Second;
            }
            set
            {
                _Second = value;
            }
        }
        private T _Second;
    }

public struct Pair<T>:Ipair<T>{}

**List<T>
List<T> myCollection =new List<T>;//创建T类型的对象,没有定义类,实现方法和进行其他操作。

对象有下属方法和属性:

int Count ;

void  Add( T item);

void AddRange(IEnumerable<T>);//把多个项添加到集合中去。

IList<T>AsReadOnly();//给集合返回一个只读接口。

int Capacity ;//获取或设置集合可以包含的项数。

void Clear();

bool Contains(T item);//确定item是否包含在集合中。

void CopyTo(T[] array ,int index)

int IndexOf(T item) ;//获取item的索引,如果项没有包含在集合上,就返回-1;

void Insert (int index ,T item);

bool Remove();

void RemoveAt();//从集合中删除索引index处的项

            //有Animal,Cow和Chicken类,其中Animal是基类,有Feed()方法
            List<Animal> animalCollection = new List<Animal>();
            animalCollection.Add(new Cow("Jack"));
            animalCollection.Add(new Chicken("Vera"));
            foreach (Animal myAnimal in animalCollection)
            {
                myAnimal.Feed();
            }
**对泛型列表进行排序和搜索【不熟悉】

使用泛型接口IComparer<T>和IComparable<T>

int IComparable<T>      CompareTo(T otherObj)

             bool IComparable<T>   Equals(T otherObj)

             int ICompare<T>             Compare(TobjectA,TobjectB)

     bool IComparer<T>        Equals(TobjectA,TobjectB)

     int IComparer<T>            GetHashCode(T objectA)

要对List<T>排序,可以在要排序的类型上提供IComparable<T>接口,或者提供IComparer<T>接口。

一般情况下,给列表排序需要有一个方法来比较T类型的两个对象,要在列表中搜索,也要有一个方法类检查T对象,看看它是否满足某个条件。

解决以上的问题可以使用两个泛型委托  Comparison<T>:这个委托用于排序方法,其返回值类型和参数是 int method(T objectA,T objectB)

Predicate<T>:这个方法用于搜索,其返回值类型和参数是bool method(T targetObject)

*定义泛型类 :其中T是任意标识符,但一般使用T。可以在定义中包含多个类型。

*类型参数命名的指导原则:使用T作为前缀。

class MyGenericClass<T>

{//...

}

class MyGenericClass<T1,T2,T3>

{//...

}

我们不知道T1是什么,也就不能使用它的构造函数,它甚至可能没有可公共访问的默认构造函数。可以对T1进行如下的假设:T1继承自System.Object的类型或封箱到System.Object中。T1的实例不能进行有趣的操作,一般可以typeof(T1).ToString();另一个限制是,T只能用!= ,== 来运算。

    class MyGenericClass<T1, T2, T3>
    {   //T1,T2,T3可以用作变量的类型,属性或方法等成员的返回类型。
        private T1 innerT1Object;

        public MyGenericClass(T1 item)
        {
            innerT1Object = item;
        }

        public T1 InnerT1Object
        {
            get
            {
                return innerT1Object;
            }
        }
    }
        public bool Compare(T1 op1, T2 op2)
        {
            if (op1 != null && op2 != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }


*在一个类中重复一个接口实现

    public interface IContainer<T>
    {
        ICollection<T> Items
        {
            set;
            get;
        }
    }

    public class Person : IContainer<Adress>, IContainer<Phone>, IContainer<Emiall>
    {
        IContainer<Adress> IContainer<Adress>.Items  //Items属性使用一个显示接口实现多次出现,每一次参数类型都有所不同。
        {
            set{};
            get{};
        }

        IContainer<Phone> IContainer<Phone>.Items
        {
            set{};
            get{};
        }

        IContainer<Email> IContainer<Email>.Items
        {
            set{};
            get{};
        }
    }


*构造器和终结器

泛型的构造器和构造器不要求添加类型来与类的声明匹配。

    public struct Pair<T>:IPair<T>
    {
        public Pair(T first,T second)
        {
            _Second=second;
            _First=first;
        }

        //...
    }


*默认值的指定:default关键字

在上述代码中,省略了属性的定义。因为在struct中,属性应该被全部初始化。可以使用default运算符来初始化一个字段,以便初始化属性。

    public struct Pair<T>:IPair<T>
    {
        public Pair(T first,T second)
        {
            _Second=second;
            _First=default(T);//使用default运算符
        }

        //...
    }

**多个类型参数

    //多个参数类型的泛型接口
    interface Ipair<TFirst,TSecond>
    {
        TFirst first { set; get; }
        TSecond second { set; get; }
    }

    //实现一个多个参数类型的泛型接口
    public struct Pair<TFirst,TSecond> : Ipair<TFirst,TSecond>
    {
        public Pair(TFirst first, TSecond second)
        {
            _First = first;
            _Second = second;
        }
        public TFirst first
        {
            get
            {
                return _First;
            }
            set 
            {
                _First = value; 
            }
        }
        private TFirst _First;

        public TSecond second
        {
            get
            {
                return _Second;
            }
            set
            {
                _Second = value;
            }
        }
        private TSecond _Second;
    }

            //使用多个参数类型的泛型
            Pair<int ,string> historycalEvent=new Pair<int,string>(1914,"hello world!!");
            Console.WriteLine("{0}:{1}",historycalEvent.first,historycalEvent.second);

*元数【不熟】

在C#4.0中,CLR定义了9个新的泛型类型:Tuple。因为元数不同(每个类都有不同数量的类型参数)可以使用隐式类型参数来创建

public class Tuple{}

public class Tuple<T1>:IStructuralEquatable,IStructuralComparable,IComparable{}

public class Tuple<T1,T2>:……{}

.....

public class Tuple<T1,T2,T3,T4...T7,TReset>:...{}

Tuple <string ,Contact> keyValuePair;

//当Tuple变大时,creat()工厂方法有明显的优势

keyValuePair=Tuple.Create("5555",new Contact("Inigo Montaya"));

keyValuePair=new Tuple<string,Contact>("5555",new Contact("Inigo MOntaya"));


*嵌套泛型:使用规则与嵌套类规则一样。


*下面用于泛型类参数的类型称为无绑定(unboundded)类型,因为没有对他们进行任何约束。通过约束,可以把类型限制为实例化泛型类。

    public class BinaryTree<T>
    {
        public BinaryTree(T item)
        {
            Item = item;
        }

        public T Item
        {
            get;
            set;
        }

        public Pair<BinaryTree<T>> SubItems
        {
            get{return _SubItem;}
            set
            {
                IComparable<T> first;
                //Error:如果没有IComparable<T>,value.first.Item看做是Object类,没有CompareTo的方法,必须显示转换;
                //虽然现在没有编译错误,但是如果声明一个BinaryTree类的变量,然后没有提供一个没有实现IComparable<T>接口的类型参数,
                //就会发生执行时错误。
                //为了避免这一点错误,C#允许泛型类中声明的每一个类型参数提供一个可选的约束列表。参数的要求用于限制从中派生的类或接口,
                //或者限制必须存在一个默认构造器,或者限制使用一个引用/值类型约束。
                first = (IComparable<T>)value.first.Item;
                if (first.CompareTo(value.second.Item) < 0)
                {
                    //first is less than second;
                }
                else
                {
                    //first is large or equal to second.
                }
                _SubItem = value;
            }
        }
        private  Pair<BinaryTree<T>> _SubItem;
    }

*在类定义中,可以使用where关键字类实现。where T:constraint1,constraint2,约束必须在继承说明符后面。


class MyGenericClass<T> where T:constraint1,constraint2

{//...

}

可有有多个where,多个where之间不存在逗号,多个where是&& 关系

class MyGenericClass<T1,T2> where T1:constraint1 where T2:constraint2

{//....

}

**可用约束:

struct

class

base-class 类型必须是基类或者继承自基类。可以给这个约束提供任意类名。

interface 类型必须是接口或者实现了接口

new() 类型必须有一个公共的无参构造函数,必须是类型指定的最后一个约束。

**接口约束:规定某个数据类型必须实现某个接口,需要声明一个接口约束。有了这种约束之后,甚至不需要执行转型,就可以调用一个显式的接口成员实现。

在上述代码中,为解决执行错误可以使用接口约束。确保T类型参数实现了ICompareTo<T>接口。

public class BinaryTree<T> where T:System.IComparable<T>

first = value.first.Item;  //这句话也不会出现编译错误。

添加上述接口约束之后,编译器会确保每次使用BinaryTree类的时候,都必须指定一个实现了IComparable<T>接口的类型参数。

除此之外,在调用CompareTo()方法之前,不需要显示转型。

**基类约束:需要将构建的类型限制为一个特定的派生类。积累约束与接口约束基本相同,但是,假如同时指定了多个约束,那么基类约束必须第一个出现。和接口不同的是,多个基类约束是不可能的,因为类不能多继承。不能为sealed类或者某些特殊类型的结构指定基类约束。

//有Cow和Chicken类继承自Animal类

MyGenericClass<Cow> =new MyGenericClass<Cow>;//OK

MyGenericClass<String>=new MyGenericClass<String>;//不能编译

**struct /class 约束:限制参数类型为一个值类型或者引用类型。编译器不允许在约束中将System.ValueType指定为基类。而是用关键字struct或者class来指定值类型或者引用类型。

public struct Nullable<T>:IFormattable,IComparable,IComparable<Nullable<T>>,INullablewhere T:Struct  //类型参数必须是一个值类型

{...

}

struct约束中,禁止将System.Nullable<T>作为参数类型。禁止  int??number;

**构造器约束:它指定的类型参数必须有一个默认构造器,只能对默认构造器进行约束,不能为带参数的构造器指定约束。

    public class EntityBase<Tkey>
    {
        public Tkey Key
        {
            get;
            set;
        }
    }

    public class EntityDictionary<Tkey, Tvalue>:
        Dictionary<Tkey,Tvalue>
        where Tkey :IComparable<Tkey>,IFormattable
        where Tvalue :EntityBase<Tkey>,new()   //使用new()
    {
        //...
        public Tvalue New(Tkey key)
        {
            Tvalue newEntity = new Tvalue();//默认的构造函数
            newEntity.Key = key;
            Add(newEntity.Key, newEntity);//继承了Dictionary所以可以直接使用方法。
            return newEntity;
        }
    }

**约束继承

约束可以由一个派生类继承,但是必须在派生类中显示的指定这些约束。在继承的情况下,不仅可以保留基类本来的约束(必须),还可以添加额外的约束。

**可以通过base-class约束,把一个参数用作另一个参数的的约束

//其中,T2必须与T1的类型相同,或者继承自T1。这成为裸类型约束(naked type constraint),表示为一个泛型类型参数用作另一个类型参数的约束

class MyGenericClass<T1,T2> where T2:T1

{...

}

//类型约束不能循环

class MyGenericClass<T1,T2>where T2:T1 where T1:T2  //Error

{...

}

    //Farm是一个泛型类Farm<T>,没有继承List类
    class Farm<T>:IEnumerable<T> where T:Animal
    {
        //字段
        private List<T> animals = new List<T>();
        //属性Animals定义为泛型List类,且被Animal约束或者继承自Animal。
        public List<T> Animals
        {
            get
            {
                return animals;
            }
        }
        //隐式接口实现 实现了了接口的方法
        public IEnumerator<T> GetEnumerator()
       {
           return animals.GetEnumerator();
       }
        //显示接口实现,实现IEnumerable.GetEnumerator()
        IEnumerator IEnumerable.GetEnumerator()
        {
            return animals.GetEnumerator();
        }
        //利用了抽象类Animal的方法
        public void MakeANoises()
        {
            foreach (T animal in animals)
            {
                animal.MakeANoise();
            }
        }

        public void FeedTheAnimals()
        {
            foreach (T animal in animals)
            {
                animal.Feed();
            }
        }

        public Farm<Cow> GetCows()
        {
            Farm<Cow> cowFarm = new Farm<Cow>();
            foreach (T animal in animals)
            {
                if (animal is Cow)
                {
                    cowFarm.Animals.Add(animal as Cow);
                }
            }
            return cowFarm;
        }
    }

**从泛型类中继承

如果某个类型在它新继承的基类型中受到约束,该类型就不能“解除约束”。也就是说,类型T在所继承的基类型中,该类型必须受到与基类型相同的约束。

下面的代码能运行:

class SuperFarm<T>:Farm<T>

where T:SuperCow   //SuperCow是Animal的子集,而T受到的是Animal。所以可以编译。

下面是代码不能运行:

class SuperFarm<T>:Farm<T>

where T:struct

如果继承了一个泛型类型,就必须提供所有必须的类型信息,这可以使用其他泛型类参数的形式来提供。

public class Cards:List<Card>,ICloneable  //编译成功。

{...

}

public class Cards:List<T>,ICloneable //编译错误,因为没有提供T的信息

{....

}

*不允许 :委托和枚举类型的约束是无效的。

不支持OR条件。

不允许运算符约束。

**泛型方法:即使包容类不是泛型类,或者方法包含的类型参数不在泛型类的类型参数列表中,也依然使用泛型的方法。为了定义泛型方法,需要紧接在方法名之后添加类型参数语法。

    //泛型方法
    public static class MathEx
    {
        public static T Max<T>(T first, params T[] values)
            where T : IComparable<T>
        {
            T maximum = first;
            foreach (T item in values)
            {
                if (item.CompareTo(maximum) > 0)
                {
                    maximum = item;
                }
            }

            return maximum;
        }

        public static T Min<T>(T first, params T[] values)
            where T : IComparable<T>
        {
            T minimum = first;
            foreach (T item in values)
            {
                if (item.CompareTo(minimum)< 0)
                {
                    minimum = item;
                }
            }

            return minimum;
        }
    }
*类型推断

            //显示指定类型参数
            Console.WriteLine(MathEx.Max<int>(7,409));
            Console.WriteLine(MathEx.Min<string>("Rous","Find"));
            //类型推断
            Console.WriteLine(MathEx.Max(88, 409));
            Console.WriteLine(MathEx.Min("aRous", "aFind"));
            Console.ReadKey();
**约束的指定

泛型方法也可以指定约束,将where放在方法头的后面,{} 前面。

***协变形和逆变性:

如果用不同的类型参数声明同一个泛型,变量不是类型兼容的,即使是讲一个较具体的类型付给一个比较泛化的类型,就说他们是协变量(covariant)。同时,编译器禁止逆变性(contravariance).换言之,就是不允许将教泛化的类型赋给教具体的类型。

***在C#4.0中使用out类型参数修饰符允许协变性。【只读】

***在C#4.0中使用in类型参数修饰符允许逆变性。【只写】

//在单个泛型类中合并协变性和逆变性。

interface TConvertible <in TSource, out TTarget>

{

TTarget Convert(TSource source);

}

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值