C#泛型

1.什么是泛型
  应该都听过刘谦的魔术《魔壶》吧,想要什么味道的饮料都可以倒出来。在C#中,可以将泛型比作刘谦手上的壶,我们需要的类型比作壶倒出来的饮料。在碰到一些功能相似,类型不同的方法时,我们就可能会用到泛型。当然object也能达到泛型的效果,但是泛型不是object,他们有很大的区别。

2.泛型和方法
  在之前上网课的时候有老师举出了一些代码示例,在这就先借鉴一下:
  首先创建一个类

using System;

namespace Generic
{
    public class MyGenericClass
    {
        public static void showInt(int iParameter)
        {
            Console.WriteLine("This is {0}, type = {1}, param={2}",
                typeof(MyGenericClass).Name, iParameter.GetType().Name, iParameter);
        }

        public static void showString(string sParameter)
        {
            Console.WriteLine("This is {0}, type = {1}, param={2}",
                typeof(MyGenericClass).Name, sParameter.GetType().Name, sParameter);
        }

        public static void showDateTime(DateTime dtParameter)
        {
            Console.WriteLine("This is {0}, type = {1}, param={2}",
                typeof(MyGenericClass).Name, dtParameter.GetType().Name, dtParameter);
        }
    }
}

  调用一下:

using System;

namespace Generic
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            MyGenericClass.showInt(1);
            MyGenericClass.showString("2");
            MyGenericClass.showDateTime(DateTime.Now);

            Console.ReadKey();
        }
    }
}

  输出结果:

This is MyGenericClass, type = Int32, param=1
This is MyGenericClass, type = String, param=2
This is MyGenericClass, type = DateTime, param=2019/7/1 15:43:54

因为object是所有类型的父类,根据继承的原则,父类可以充当子类来使用,所以MyGenericClass也可以改为:

public static void showobject(object oParameter)
{
	Console.WriteLine("This is {0}, type = {1}, param={2}",
	typeof(MyClassTemplate).Name, oParameter.GetType().Name, oParameter);
}

上文有说道object也可以做到跟泛型类似的功能,通过简单的修改就可以将object改写为泛型方法:
  我们将MyGenericClass中的object方法使用泛型来改写:

public static void showGeneric<T>(T tParameter)
{
	Console.WriteLine("This is {0}, type = {1}, param={2}",
	typeof(MyClassTemplate).Name, tParameter.GetType().Name, tParameter);
}

  调用一下:

using System;

namespace Generic
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            //MygenericClasee.showGeneric<int>(1);
            MyGenericClass.showGeneric(1);
            MyGenericClass.showGeneric("2");
            MyGenericClass.showGeneric(DateTime.Now);

            Console.ReadKey();
        }
    }
}

  输出结果

This is MyGenericClass, type = Int32, param=1
This is MyGenericClass, type = String, param=2
This is MyGenericClass, type = DateTime, param=2019/7/1 15:43:54

那么object到底和泛型有什么区别呢,我们来看下:
  新建一个类用于记录程序运行时间:

using System;
using System.Diagnostics;

namespace Generic
{
    public class Monitor
    {
        public static void Show()
        {
            Console.WriteLine("======================");

            int iValue = 123456;
            long intSecond = 0;
            long objectSecond = 0;
            long genericSecond = 0;

            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 500000000; i++)
            {
                ShowInt(iValue);
            }
            watch.Stop();
            intSecond = watch.ElapsedMilliseconds;

            watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 500000000; i++)
            {
                ShowObject(iValue);
            }
            watch.Stop();
            objectSecond = watch.ElapsedMilliseconds;

            watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 500000000; i++)
            {
                ShowGeneric(iValue);
            }
            watch.Stop();
            genericSecond = watch.ElapsedMilliseconds;

            Console.WriteLine("commonSecond={0},objectSecond={1},genericSecond={2}"
            , intSecond, objectSecond, genericSecond);
        }
		//这里在方法中不要写任何代码,执行时间过久
        private static void ShowInt(int iParameter){}

        private static void ShowObject(object sParameter){}

        private static void ShowGeneric<T>(T tParameter){}
    }
}

各个方法运行5亿次后的输出结果:

commonSecond=1591,objectSecond=2593,genericSecond=1694

我们可以看出执行速度(也可以说是性能)子类型≈泛型<object,这是因为在使用object的时候会涉及到一个装箱拆箱的操作,在做拆箱装箱操作是会损耗程序的性能。还有一点泛型和子类型执行速度那个快,需要看代码的复杂程度。

为什么泛型的性能高于object?
泛型是延时声明的,在定义的时候不需要指定具体的参数类型,只有在调用的时候才会去制定具体的参数类型,类似与List,和Dictionary

 Console.WriteLine(typeof(List<>));
 Console.WriteLine(typeof(Dictionary<,>));
System.Collections.Generic.List`1[T]
System.Collections.Generic.Dictionary`2[TKey,TValue]

从输出结果来看List和Dictionary后方~跟的是数据类型,只是一个占位符。在经过JIT编译的时候才会给与对应的数据类型。
相似的还有泛型类,泛型接口,泛型委托等。
3.泛型约束
新建类MyGeneric.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Generic
{
	//泛型接口
	public interface IInterface<T>
	{
		T genericInterface(T t);
	}
	//泛型接口继承,参数类型可以为泛型
	public interface ISubInterface<T> : IInterface<T>
    {
    }
    //普通接口
    public interface INormalInterface
    {
        void Hi();
    }
	
    public class MyGeneric : ISubInterface<string>
    {
        public string genericInterface(string t)
        {
            return t;
        }
    }

    public class cooker
    {
        public int id { get; set; }
        public string name { get; set; }

        public void cook()
        {
            Console.WriteLine("I can cook");
        }
    }

    public class spoken : cooker, INormalInterface
    {
        public void Hi()
        {
            Console.WriteLine("Hi");
        }
    }

    public class emily : spoken
    {
    }
}

Main函数中赋值,调用泛型方法

	emily emily = new emily()
	{
    	id = 345,
		name = "emily"
		};
	MyClassTemplate.ShowGeneric(emily);

修改泛型方法

public static void ShowGeneric<T>(T tParameter) where T : emily
{
	Console.WriteLine("This is {0}, type = {1}, param={2}",
		typeof(MyClassTemplate).Name, tParameter.GetType().Name, tParameter);
	var id = tParameter.id;
	var name = tParameter.name;
	tParameter.Hi();
}

修改后的泛型方法可以打印出指定传入参数的属性值。泛型约束中emily有id和name属性,但是如果在使用object方法的时候,object并没有id和name属性,所以会报错。
3.5种泛型约束

where T : struct类型必须是一种值类型(struct)
here T :class类型必须是一种引用类型(class)
where T : new()类型必须有一个无参数的构造器
where T : class_name类型可以是class_name或者是它的一个子类
where T : interface_name类型必须实现指定的接口

a.基类约束

/// <summary>
/// 基类约束:T必输是Class类型或者Class的子类
/// </summary>
public class MyClass<T>(T tParameter) where T:Class{}

///example
public static void ShowGeneric<T>(T tParameter) where T : emily
{
	Console.WriteLine("This is {0}, type = {1}, param={2}",
		typeof(MyClassTemplate).Name, tParameter.GetType().Name, tParameter);
	var id = tParameter.id;
	var name = tParameter.name;
	tParameter.Hi();
}

MyClassTemplate.ShowGeneric(emily);

b.接口约束

/// <summary>
/// 接口约束
/// </summary>
public static T Get<T>(T t) where T : IGemericInterface
{
      t.XXX();
      return t;
}

c.引用类型约束

/// <summary>
 /// 引用类型约束
 /// </summary>
 public static T Get<T>(T t) where T : class
 {
       return t;
}

d.值类型约束 struct

/// <summary>
/// 值类型类型约束
/// </summary>
public static T Get<T>(T t) where T : struct
{
      return t;
}

e.无参数构造函数约束 new()

/// <summary>
/// new()约束
/// </summary>
public static T Get<T>(T t) where T : new()
{
     return t;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值