.NET学习笔记(一)----泛型的引入、优势、设计思想、原理、应用

脑图:

在这里插入图片描述

一、泛型的引入,优势

1.泛型是一种宽泛的、不确定的类型
2.它无处不在
3.与普通类型的差异:
泛型与普通类型的区别
4.支持传递多类型的参数类型
在这里插入图片描述

PS:在一个方法中对一个对象进行强制转换【转换为People people =
(People)传入参数】,如果方法的传入参数为Object类型,那么给他传入Chinese类型的参数编译时不会报错,但是运行时会发生异常,而如果为泛型方法的话直接在方法中强转传入参数的类型的话会发生编译报错。

代码示例:

/// <summary>
/// object  :引用类型
/// </summary>
/// <param name="oParameter"></param>
public static void ShowObject(object oParameter)
{
    Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={oParameter.GetType().Name},type={oParameter}");
}


/// <summary>
/// 泛型方法
/// </summary>
/// <typeparam name="T">类型变量  参数类型</typeparam>
/// <param name="tParameter"></param>
public static void Show<T>(T tParameter)
{
    Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={tParameter.GetType().Name},type={tParameter}");
}

PS:

引用类型----声明后存储到托管堆
值类型----存储到线程栈

二、泛型的声明与调用

1、声明
①、泛型方法:

–在方法名称后面多了一个尖括号,尖括号中有占位符
例如:Show<T>(T Value)

②、延迟声明:

–声明的时候,只是给一个占位符T,T为不确定类型。
–调用的时候,指定具体类型,调用(使用)的时候,必须要确定类型,你说什么就是什么;

③、占位符(不可使用关键字):

T—类型参数—类型变量

④、类型参数当做方法的参数的时候,用于明确参数类型
在这里插入图片描述

⑤、尖括号中可存在多个类型参数:

public class Test<T,X,S>

2.调用
①、调用时需要多一个<>,尖括号中指定的类型要和传递的参数类型一致。如:

Show<string>(“value”)
Show<int>(123)

②、如果可以通过参数推导出类型—尖括号可以省略。如:

Show("value")
Show(123)

三、泛型的特点+原理----在底层如何支持

①、在高级语言中,定义的泛型T,在计算机执行的执行的时候,一定要是一个具体的类型

②、在底层如何支持?
在底层看到 生成的结果是:
代码:

Console.WriteLine(typeof(int));

输出结果:
在这里插入图片描述
代码:

Console.WriteLine(typeof(Dictionary<,>));

输出结果:
在这里插入图片描述
在底层—生成了带有`1`2 `3…`4.`5…

③、编译器必须要能够支持泛型

----C# 2.0 .NET Framework 2.0 Visual Studio 2005 开始支持

④、CLR 运行时环境也必须要支持泛型

⑤、泛型是由框架升级支持的

----.net中泛型不是语法糖,但是在调用时可以省略类型属于语法糖
----语法糖:编译器提供的便捷功能

四、泛型的多种应用

①.泛型方法----可以一个方法满足不同类型的需求

/// <summary>
/// 泛型方法
/// </summary>
/// <typeparam name="T">类型变量  参数类型</typeparam>
/// <param name="tParameter"></param>
public static void Show<T>(T tParameter)
{
    Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={tParameter.GetType().Name},type={tParameter}");
}

//调用
Show<string>("value");
Show<DateTime>(DateTime.Now);

②.泛型接口----可以一个接口满足不同类型的需求

/// <summary>
/// 泛型接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface GenericInterfac<T>
{
}

//调用
GenericInterfac<string> sgenericInterfac = null;
GenericInterfac<DateTime> dtgenericInterfac = null;

③.泛型类------可以一个类型满足不同类型的需求

/// <summary>
/// 泛型类
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericClass<T>
{
}

//调用
GenericClass<string> sGenericClass = new GenericClass<string>();
GenericClass<DateTime> dtsGenericClass = new GenericClass<DateTime>();

④.泛型委托----可以一个委托满足不同类型的需求

/// <summary>
/// 泛型委托
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public delegate T Genericdelegate<T>();

//调用
Genericdelegate<string> sGenericdelegate = null;
Genericdelegate<DateTime> dtGenericdelegate = null;

PS:
1.占位符可用于当做返回值,比如:
①.接口中:

public interface GenericInterfac<T>
{
    //T当做返回值
    public T Show();
}

②.类中:

public class GenericClass<T>
{
    public T Show()
	{
        return default(T);
	}

    public T Show2(T t)
	{
        return t;
	}
}

2.如果需要当做父类去继承,继承时必须指定类型

/// <summary>
/// 父类
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class GenericAbstractClass<T>
{
}

①.普通类:

public class ChildClass : GenericAbstractClass<int>
{
}

②.子类同样是泛型的话,可使用子类的占位符

public class ChildClass<S> : GenericAbstractClass<S>
{
}

3.实现泛型接口

/// <summary>
/// 实现的接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface GenericInterfac<T>
{
}

①.普通类实现

public class ChildClass : GenericInterfac<int>
{
	public int Show()
	{
		throw new NotImplementedException();
	}
}

②.泛型类实现

public class ChildClass<S> : GenericInterfac<S>
{
	public S Show()
	{
		throw new NotImplementedException();
	}
}

五、泛型约束

类:

public class People
{
	public int Id { get; set; }
    public string Name { get; set; }
}

public class People2
{
	//带参构造函数
	public People2(int id)
	{
	}
	public int Id { get; set; }
    public string Name { get; set; }
}

public class Chinese : People , ISports
{
	public void Tradition()
    {
    	Console.WriteLine("仁义礼智信,温良恭俭让");
    }
    public void SayHi()
   	{
       Console.WriteLine("吃了么?");
    }

    public void Pingpang()
    {
  		Console.WriteLine("打乒乓球...");
    }

    public void Work()
    {
    	Console.WriteLine("Work...");
    }
}

接口:

public interface ISports
{
    void Pingpang();
}

1、基类约束

①.where后面指定类为参数类型
②.可以传递指定类与其子类
③.因为约束的存在,只要能成功传入参数,就一定没问题,所以不会存在类型安全问题。

展示用的方法:

//把类型参数当做People
public static void Show<T>(T tParameter) where T:People
{
	Console.WriteLine($"People.Id = {tParameter.Id}")
}

调用:

People people = new People()
{
	Id = 123,
	Name = "张三"
};
Show<People>(people);

Chinese chinese = new Chinese()
{
	Id = 123,
	Name = "李四"
};
Show<Chinese>(chinses);

2、接口约束

①.where后面指定接口为参数类型
②.只能传递指定接口或者是实现过这个接口的类
③.接口约束可以获取、增加接口中的功能

展示用的方法:

public static void ShowInterface<T>(T tParameter) where T:ISports
{
	//调用接口中的功能
	tParameter.Pingpang();
}

调用:

Chinese chinese = new Chinese()
{
	Id = 123,
	Name = "李四"
};
ShowInterface<Chinese>(chinses);

3、引用类型约束

①.就只能传递引用类型,比如where后指定class就只能传递类

展示用的方法:

public static void ShowClass<T>(T tParameter) where T:class
{
}

调用:

People people = new People()
{
	Id = 123,
	Name = "张三"
};
ShowClass<People>(people);

Chinese chinese = new Chinese()
{
	Id = 123,
	Name = "李四"
};
ShowClass<Chinese>(chinses);

4、值类型约束

①.就只能传递值类型,比如int、DateTime

展示用的方法:

public static void ShowStruct<T>(T tParameter) where T:struct
{
}

调用:

ShowStruct<int>(123);
ShowStruct<DateTime>(DateTime.Now);

5、无参数构造函数约束

----没有有参数构造函数约束

①.必须有一个无参数构造函数才能当做参数传入

展示用的方法:

public static void ShowNew<T>(T tParameter) where T:new()
{
	T t = new T();
}

调用:

People people = new People()
{
	Id = 123,
	Name = "张三"
};
ShowNew<People>(people);

//报错
People2 people2 = new People2()
{
	Id = 123,
	Name = "张三"
};
ShowNew<People2>(people2);

6、枚举约束

①.必须是枚举才能传入

展示用的方法:

public static void ShowEnum<T>(T tParameter) where T:Enum
{
	T t = new T();
}

调用:

public Enum UserType
{
	Chinese = 1,
	Japanese = 2
}
ShowEnum<Enum>(User.Chinese);

7、其他补充

①、要么是父子关系,要么是同一个类型

展示用的方法:

public static void ShowParent<T,S>(T tParameter , S sParameter) where T:S
{
}

调用:

People people = new People()
{
	Id = 123,
	Name = "张三"
};

Chinese chinese = new Chinese()
{
	Id = 123,
	Name = "李四"
};

//父子关系调用
//子类在前,父类在后
ShowParent<Chinese , People>(chinese , people);

//同类型调用
ShowParent<Chinese , Chinese>(chinese , chinese);

六、泛型缓存

----泛型缓存的本质是泛型类

泛型缓存:

//泛型缓存--可以根据不同的类型生成一个新的类的副本,几种类型就会生成几个副本;
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(GenericCache<int>.GetCache()); //GenericCacheInt
    Thread.Sleep(10);
    Console.WriteLine(GenericCache<long>.GetCache());// GenericCachelong
    Thread.Sleep(10);
    Console.WriteLine(GenericCache<DateTime>.GetCache());
    Thread.Sleep(10);
    Console.WriteLine(GenericCache<string>.GetCache());
    Thread.Sleep(10);
    Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
    Thread.Sleep(10);
}

/// <summary>
///泛型缓存:
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericCache<T>
{
	//每种类型都会执行一次静态方法,且执行一次
    static GenericCache()
    {
        Console.WriteLine("This is GenericCache 静态构造函数");
        //_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff")); 
        _TypeTime = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}";
    }

    private static string _TypeTime = "";

    public static string GetCache()
    {
        return _TypeTime;
    }

普通的字典缓存(用于比较):

//字典缓存
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(DictionaryCache.GetCache<int>()); //GenericCacheInt
        Thread.Sleep(10);
        Console.WriteLine(DictionaryCache.GetCache<long>());// GenericCachelong
        Thread.Sleep(10);
        Console.WriteLine(DictionaryCache.GetCache<DateTime>());
        Thread.Sleep(10);
        Console.WriteLine(DictionaryCache.GetCache<string>());
        Thread.Sleep(10);
        Console.WriteLine(DictionaryCache.GetCache<GenericCacheTest>());
        Thread.Sleep(10);
    }
}

/// <summary>
/// 字典缓存:静态属性常驻内存
/// </summary>
public class DictionaryCache
{
    private static Dictionary<Type, string> _TypeTimeDictionary = null;

    //静态构造函数在整个进程中,执行且只执行一次;
    static DictionaryCache()
    {
        Console.WriteLine("This is DictionaryCache 静态构造函数");
        _TypeTimeDictionary = new Dictionary<Type, string>();
    }
    public static string GetCache<T>()
    {
        Type type = typeof(T);
        if (!_TypeTimeDictionary.ContainsKey(type))
        {
            _TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}";
        }
        return _TypeTimeDictionary[type];
    }
}

七、泛型的协变、逆变

----协变逆变只针对泛型接口和泛型委托

类:

/// <summary>
/// 动物
/// </summary>
public class Animal
{
    public int Id { get; set; }
}

/// <summary>
/// Cat 猫
/// </summary>
public class Cat : Animal
{
    public string Name { get; set; }
}

1.泛型存在不友好、不协调的地方(list本身就是一个泛型集合)。

//任何子类都可以使用父类来声明
//一只猫可以是一只动物
Animal animal2 = null;
animal2 = new Cat();

//下面的代码会报错:
//一群猫没办法成为一群动物
List<Animal> animalList2 = null;
// //不能在左边用父类
animalList2 = new List<Cat>();

在这里插入图片描述
普通的解决办法:

List<Animal> animalList3 = new List<Cat>().Select(c => (Animal)c).ToList();

2.协变
官方文档中的IEnumerable:

public interface IEnumerable<out T> : IEnumerable
{
    new IEnumerator<T> GetEnumerator();
}

IEnumerable的使用:

//IEnumerable 也经常把他当成一个集合来用
//协变:让右边用子类,能让左边用父类
IEnumerable<Animal> animalList1 = new List<Animal>();
IEnumerable<Animal> animalList2 = new List<Cat>();

OUT:

/// <summary>
/// out 协变 只能是返回结果 ,还是int 也是一种高级约束,避免出现问题
/// 泛型T 就只能做返回值; 不能做参数; 
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListOut<out T>
{
    T Get();

    //void Show(T t);
}

public class CustomerListOut<T> : ICustomerListOut<T>
{
    public T Get()
    {
        return default(T);
    }
	
	//public void Show(T t)
    //{
	//
    //}
}

//out:修饰类型参数;就可以让右边用子类,能让左边用父类
ICustomerListOut<Animal> customerList = new CustomerListOut<Cat>(); 

3.逆变
IN:

/// <summary>
/// T 就只能做参数  不能做返回值
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListIn<in T>
{
    //T Get();

    void Show(T t);
}

public class CustomerListIn<T> : ICustomerListIn<T>
{
    //public T Get()
    //{
    //    return default(T);
    //}

    public void Show(T t)
    {

    }
}

//逆变:就可以让右边用父类;左边用子类
//逆变: In 类型参数只能做参数 ,不能做返回值
ICustomerListIn<Cat> customerList = new CustomerListIn<Animal>();

4.协变逆变同时存在

public interface IMyList<in inT, out outT>
{
    void Show(inT t);
    outT Get();
    outT Do(inT t);
}

//协变逆变的存在,就是为了满足常规场景添加一个避开风险的约束; 
{
    IMyList<Cat, Animal> myList1 = new MyList<Cat, Animal>();
    IMyList<Cat, Animal> myList2 = new MyList<Cat, Cat>();//协变 
    IMyList<Cat, Animal> myList3 = new MyList<Animal, Animal>();//逆变 
    IMyList<Cat, Animal> myList4 = new MyList<Animal, Cat>();//协变+逆变
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Java泛型是Java 5引入的新特性,可以提高代码的可读性和安全性,降低代码的耦合度。泛型是将类型参数化,实现代码的通用性。 一、泛型的基本语法 在声明类、接口、方法时可以使用泛型泛型的声明方式为在类名、接口名、方法名后面加上尖括号<>,括号中可以声明一个或多个类型参数,多个类型参数之间用逗号隔开。例如: ```java public class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T> { T getData(); void setData(T data); } public <T> void genericMethod(T data) { System.out.println(data); } ``` 其中,`GenericClass`是一个泛型类,`GenericInterface`是一个泛型接口,`genericMethod`是一个泛型方法。在这些声明中,`<T>`就是类型参数,可以用任何字母代替。 二、泛型的使用 1. 泛型类的使用 在使用泛型类时,需要在类名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericClass<String> gc = new GenericClass<>(); gc.setData("Hello World"); String data = gc.getData(); ``` 在这个例子中,`GenericClass`被声明为一个泛型类,`<String>`指定了具体的类型参数,即`data`字段的类型为`String`,`gc`对象被创建时没有指定类型参数,因为编译器可以根据上下文自动推断出类型参数为`String`。 2. 泛型接口的使用 在使用泛型接口时,也需要在接口名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericInterface<String> gi = new GenericInterface<String>() { private String data; @Override public String getData() { return data; } @Override public void setData(String data) { this.data = data; } }; gi.setData("Hello World"); String data = gi.getData(); ``` 在这个例子中,`GenericInterface`被声明为一个泛型接口,`<String>`指定了具体的类型参数,匿名内部类实现了该接口,并使用`String`作为类型参数。 3. 泛型方法的使用 在使用泛型方法时,需要在方法名前面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java genericMethod("Hello World"); ``` 在这个例子中,`genericMethod`被声明为一个泛型方法,`<T>`指定了类型参数,`T data`表示一个类型为`T`的参数,调用时可以传入任何类型的参数。 三、泛型的通配符 有时候,我们不知道泛型的具体类型,可以使用通配符`?`。通配符可以作为类型参数出现在方法的参数类型或返回类型中,但不能用于声明泛型类或泛型接口。例如: ```java public void printList(List<?> list) { for (Object obj : list) { System.out.print(obj + " "); } } ``` 在这个例子中,`printList`方法的参数类型为`List<?>`,表示可以接受任何类型的`List`,无论是`List<String>`还是`List<Integer>`都可以。在方法内部,使用`Object`类型来遍历`List`中的元素。 四、泛型的继承 泛型类和泛型接口可以继承或实现其他泛型类或泛型接口,可以使用子类或实现类的类型参数来替换父类或接口的类型参数。例如: ```java public class SubGenericClass<T> extends GenericClass<T> {} public class SubGenericInterface<T> implements GenericInterface<T> { private T data; @Override public T getData() { return data; } @Override public void setData(T data) { this.data = data; } } ``` 在这个例子中,`SubGenericClass`继承了`GenericClass`,并使用了相同的类型参数`T`,`SubGenericInterface`实现了`GenericInterface`,也使用了相同的类型参数`T`。 五、泛型的限定 有时候,我们需要对泛型的类型参数进行限定,使其只能是某个类或接口的子类或实现类。可以使用`extends`关键字来限定类型参数的上限,或使用`super`关键字来限定类型参数的下限。例如: ```java public class GenericClass<T extends Number> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T extends Comparable<T>> { T getData(); void setData(T data); } ``` 在这个例子中,`GenericClass`的类型参数`T`被限定为`Number`的子类,`GenericInterface`的类型参数`T`被限定为实现了`Comparable`接口的类。 六、泛型的擦除 在Java中,泛型信息只存在于代码编译阶段,在编译后的字节码中会被擦除。在运行时,无法获取泛型的具体类型。例如: ```java public void genericMethod(List<String> list) { System.out.println(list.getClass()); } ``` 在这个例子中,`list`的类型为`List<String>`,但是在运行时,`getClass`返回的类型为`java.util.ArrayList`,因为泛型信息已经被擦除了。 七、泛型的类型推断 在Java 7中,引入了钻石操作符<>,可以使用它来省略类型参数的声明。例如: ```java List<String> list = new ArrayList<>(); ``` 在这个例子中,`ArrayList`的类型参数可以被编译器自动推断为`String`。 八、总结 Java泛型是一个强大的特性,可以提高代码的可读性和安全性,降低代码的耦合度。在使用泛型时,需要注意它的基本语法、使用方法、通配符、继承、限定、擦除和类型推断等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

焦糖丨玛奇朵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值