VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • C#教程之泛型(2)

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace MyGeneric
 8 {
 9     public class Cat :Animal
10     {
11         public string Name { get; set; }
12     }
13 }
复制代码
复制代码
复制代码

 在Main()方法可以这样调用:

复制代码
复制代码
复制代码
 1 // 直接声明Animal类
 2 Animal animal = new Animal();
 3 // 直接声明Cat类
 4 Cat cat = new Cat();
 5 // 声明子类对象指向父类
 6 Animal animal2 = new Cat();
 7 // 声明Animal类的集合
 8 List<Animal> listAnimal = new List<Animal>();
 9 // 声明Cat类的集合
10 List<Cat> listCat = new List<Cat>();
复制代码
复制代码
复制代码

 那么问题来了:下面的一句代码是不是正确的呢?

1 List<Animal> list = new List<Cat>();

 可能有人会认为是正确的:因为一只Cat属于Animal,那么一群Cat也应该属于Animal啊。但是实际上这样声明是错误的:因为List<Cat>和List<Animal>之间没有父子关系。

这时就可以用到协变和逆变了。

1 // 协变
2 IEnumerable<Animal> List1 = new List<Animal>();
3 IEnumerable<Animal> List2 = new List<Cat>();

 F12查看定义:

可以看到,在泛型接口的T前面有一个out关键字修饰,而且T只能是返回值类型,不能作为参数类型,这就是协变。使用了协变以后,左边声明的是基类,右边可以声明基类或者基类的子类。

协变除了可以用在接口上面,也可以用在委托上面:

1 Func<Animal> func = new Func<Cat>(() => null);

 除了使用.NET框架定义好的以为,我们还可以自定义协变,例如:

复制代码
复制代码
复制代码
 1 /// <summary>
 2 /// out 协变 只能是返回结果
 3 /// </summary>
 4 /// <typeparam name="T"></typeparam>
 5 public interface ICustomerListOut<out T>
 6 {
 7      T Get();
 8 }
 9 
10 public class CustomerListOut<T> : ICustomerListOut<T>
11 {
12      public T Get()
13      {
14          return default(T);
15      }
16 }
复制代码
复制代码
复制代码

 使用自定义的协变:

1 // 使用自定义协变
2 ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
3 ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>();

 在来看看逆变。

在泛型接口的T前面有一个In关键字修饰,而且T只能方法参数,不能作为返回值类型,这就是逆变。请看下面的自定义逆变:

复制代码
复制代码
复制代码
 1 /// <summary>
 2 /// 逆变 只能是方法参数
 3 /// </summary>
 4 /// <typeparam name="T"></typeparam>
 5 public interface ICustomerListIn<in T>
 6 {
 7      void Show(T t);
 8 }
 9 
10 public class CustomerListIn<T> : ICustomerListIn<T>
11 {
12      public void Show(T t)
13      {
14      }
15 }
复制代码
复制代码
复制代码

 使用自定义逆变:

1 // 使用自定义逆变
2 ICustomerListIn<Cat> customerListCat1 = new CustomerListIn<Cat>();
3 ICustomerListIn<Cat> customerListCat2 = new CustomerListIn<Animal>();

 协变和逆变也可以同时使用,看看下面的例子:

复制代码
复制代码
复制代码
 1 /// <summary>
 2 /// inT 逆变
 3 /// outT 协变
 4 /// </summary>
 5 /// <typeparam name="inT"></typeparam>
 6 /// <typeparam name="outT"></typeparam>
 7 public interface IMyList<in inT, out outT>
 8 {
 9      void Show(inT t);
10      outT Get();
11      outT Do(inT t);
12 }
13 
14 public class MyList<T1, T2> : IMyList<T1, T2>
15 {
16 
17      public void Show(T1 t)
18      {
19           Console.WriteLine(t.GetType().Name);
20      }
21 
22      public T2 Get()
23      {
24           Console.WriteLine(typeof(T2).Name);
25           return default(T2);
26       }
27 
28       public T2 Do(T1 t)
29       {
30            Console.WriteLine(t.GetType().Name);
31            Console.WriteLine(typeof(T2).Name);
32            return default(T2);
33        }
34  }
复制代码
复制代码
复制代码

 使用:

1 IMyList<Cat, Animal> myList1 = new MyList<Cat, Animal>();
2 IMyList<Cat, Animal> myList2 = new MyList<Cat, Cat>();//协变
3 IMyList<Cat, Animal> myList3 = new MyList<Animal, Animal>();//逆变
4 IMyList<Cat, Animal> myList4 = new MyList<Animal, Cat>();//逆变+协变

 七、泛型缓存

在前面我们学习过,类中的静态类型无论实例化多少次,在内存中只会有一个。静态构造函数只会执行一次。在泛型类中,T类型不同,每个不同的T类型,都会产生一个不同的副本,所以会产生不同的静态属性、不同的静态构造函数,请看下面的例子:

复制代码
复制代码
复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace MyGeneric
 8 {
 9     public class GenericCache<T>
10     {
11         static GenericCache()
12         {
13             Console.WriteLine("This is GenericCache 静态构造函数");
14             _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
15         }
16 
17         private static string _TypeTime = "";
18 
19         public static string GetCache()
20         {
21             return _TypeTime;
22         }
23     }
24 }
复制代码
复制代码
复制代码

 然后新建一个测试类,用来测试GenericCache类的执行顺序:

复制代码
复制代码
复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 namespace MyGeneric
 9 {
10     public class GenericCacheTest
11     {
12         public static void Show()
13         {
14             for (int i = 0; i < 5; i++)
15             {
16                 Console.WriteLine(GenericCache<int>.GetCache());
17                 Thread.Sleep(10);
18                 Console.WriteLine(GenericCache<long>.GetCache());
19                 Thread.Sleep(10);
20                 Console.WriteLine(GenericCache<DateTime>.GetCache());
21                 Thread.Sleep(10);
22                 Console.WriteLine(GenericCache<string>.GetCache());
23                 Thread.Sleep(10);
24                 Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
25                 Thread.Sleep(10);
26             }
27         }
28     }
29 }
复制代码
复制代码
复制代码

 Main()方法里面调用:

1 GenericCacheTest.Show();

 结果:

从上面的截图中可以看出,泛型会为不同的类型都创建一个副本,所以静态构造函数会执行5次。 而且每次静态属性的值都是一样的。利用泛型的这一特性,可以实现缓存。



相关教程
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号