复习:J&C:泛型
所以能提高性能:(不同的泛型参数)编译时生成(不同的类),避免装箱/拆箱等类型转换 (对比:Java的泛型擦除)
class Student<T> class Student
是可以共存的。
static T grow<T>(T year) //泛型参数的声明在方法名后面 { return default(T); }
使用where关键字和冒号(:)
internal class Generic<T> where T : Person /*只能是Person及其子类*/ internal class Generic<T> where T : ISort /*只能是ISort及其实现*/
还有额外的约束
internal class Generic<T> where T : struct /*只能是值类型,由struct定义*/ internal class Generic<T> where T : class /*只能是引用类型,由class定义*/ internal class Generic<T> where T : new() /*必须包含一个公共的无参构造函数,可以new()*/为同一个Type parameter定义多个(不冲突的)约束,用逗号分割:
class MimicStack<T, TKey> //where T : class, struct 冲突了,不行 where T : ILearn, new() where TKey: new()
且new()/struct/class要放在最后。
可以获取某个类型的默认值:
array[0] = default(T);
尤其当不知道T具体为何类型时有用。
当泛型类变得冗长的时候(奇巧淫技):
using StudentMC = Student<Major<string>, Score<Major<string>, double>>;
StudentMC student = new StudentMC();
复习:泛型类还不是“类”。相同的泛型类,使用不同的类型参数,就是不同的类。
//Major<string> minor = new Major<int>(); 报错
即使类型参数之间有继承关系也不行:
Major<Person> minor = new Major<Student>();但是,为了让被定义的泛型接口和委托,可以有更广泛的应用。从.NET4.0开始,可以:
具体来说:
准确描述:一个类型参数声明了协变out(或逆变in)的泛型接口,用父类(或子类)做类型参数时,可以接受子类(或父类)做类型参数的实例。
理解(帮助记忆)in和out:
|
泛型参数用于 |
比如: |
所以 |
in |
输入 |
IComparable<T>,T由外部传入,在方法内部使用 |
内部使用T的父类是OK的 |
out |
输出 |
IEnumerable<T>,T最终是会输出到外部,供外部使用的 |
外部使用T的子类是OK的 |
interface ISort<out T> { public T GetMax(); //-------------------------------- class BubbleSort<T> : ISort<T> { public T GetMax() { return default(T); //-------------------------------- ISort<Person> iP = new BubbleSort<Student>(); Person person = iP.GetMax(); //说明:传入和返回的都是Student,能够被Person“装”(引用)
interface ISort<in T> where T : Person { public int Compare(T x, T y); //---------------------------------- class BubbleSort<T> : ISort<T> where T : Person { public int Compare(T x, T y) { Console.WriteLine(x.Name); //只要是Person的子类,一定有Name属性的 //---------------------------------- ISort<Student> iP = new BubbleSort<Person>(); //按声明传Student iP.Compare(new Student(), new Student());总的来说,就是不让后续的调用出现“类型不兼容”的问题,类似于讲继承多态时为什么只能是“父类变量装子类对象”……
复习:值类型变量不能被赋值为null
但有时候需要一个值类型变量的值为null:
int? i = 18; int j = i.Value; i = null; Console.WriteLine(i.HasValue);
演示:F12查看源代码
public struct Nullable<T> where T : struct
所以本质上,int?是Nullable<int>:
int? age = new Nullable<int>(32);
根据源代码演示/复习:构造函数、只读属性、类型转换重载、override Object方法……
NullReferenceException是最常见的异常。
演示:形成过程
class Student { public double[] Scores { get; set; }
new Student().Scores[0] = 98.5;为了避免抛出这种异常,以前我们不得不在代码中加上大量的null check语句:
if (Scores == null) { Scores = new double[10]; } else
不为null值时,返回原值;为null值时,返回 ?? 后替代值
SQL.Teacher = SQL.Teacher ?? new Teacher();
等同于:
if (SQL.Teacher == null) { SQL.Teacher = new Teacher(); }
只有 . 或者 [] 前面的变量不为null值的时候,才进行取值;否则返回null值。
string name = SQL.Teacher?.Name;
等同于:
string name = null; if (SQL.Teacher.Name != null) { name = SQL.Teacher.Name; }/*else name 仍然是null*/
可空类型后面也可以加?表示它是可空的。
public string? Name { get; set; }
如果没有?,不是可空的,VS会给警告。去除警告的方式是添加针对该属性的构造函数(#体会#:为什么这么设计?)
如果在调用时可以确认某可空类型属性不为空,可以加!去掉警告:
Console.WriteLine(new Student().Name!.Length);
如果觉得不需要这种可空引用类型检查,可以在project配置文件中全局disabled:
会在.proj中添加/更改:
<PropertyGroup> <Nullable>enable</Nullable> </PropertyGroup>
Console.WriteLine(SQL.Teacher?.Name ?? "course SQL has no teacher");
多快好省!前端后端,线上线下,名师精讲
更多了解 加: