接口声明可以有任何的访问修饰符public,internal,protected和private。
接口的成员是隐式public的,不允许有任何访问修饰符
只有类和结构才能实现接口
- 在基类列表中包括接口名称
- 为每一个接口的成员提供实现
关于实现接口:
- 如果类实现了接口,他必须实现接口的所有成员
-
如果类从基类继承并实现接口,基类列表中的基类名称必须放在任何接口之前
简单接口实例
接口是引用类型
接口不仅仅是类和结构要实现的成员列表,它是一个引用类型
从类对象引用获取接口引用的实例
这段代码输出结果为:
Calling through: object.
Calling through: interface.
接口和as运算符
当尝试强制转换类对象引用为类未实现的接口的引用,强制转换操作会抛出一个异常。可以通过使用as运算符来避免
- 如果类实现了接口,表达式返回指向接口的引用
- 如果没有,表达式返回null而不是抛出异常
实现多个接口
- 类和结构可以实现任意数量的接口
-
所有实现的接口必须列在基类列表中并以逗号分隔
实现具有重复成员的接口
如果一个类实现了多个接口,那么其中一些接口有相同签名和返回类型的成员。类可以实现单个成员来满足包含重复成员的接口
多个接口的引用
派生成员作为实现
实现接口的类可以从他的基类继承实现的代码
显示接口成员实现
使用限定接口名称来声明,有接口名称和成员名称以及他们中间的点分隔符号构成
访问显示接口成员实现
显示接口成员实现只可以通过指向接口的引用来访问,也就是说其他的类成员都不可以直接访问它们。
接口可以继承接口
接口本身可以从一个或多个接口继承
结果接口包含它声明的所有接口和所有基类的成员。
转换
- 转换时接受一个类型的值并使用他作为另一个类型的等价值的过程
- 转换后的值应和源值一样的,但他是目标类型
用户自定义转换
is运算符
is运算符语法如下,expr是源表达式
如果expr成功转换为目标类型,运算符返回true
is运算符只可用于引用转换以及装箱和拆箱转换,不能用于用户自定义转换
as运算符
as运算符语法如下,expr是源表达式
targettype是目标类型,他必须是引用类型
转换失败返回null
泛型
c#提供了五种泛型:类,结构,接口,委托和方法。前四个是类型,方法是成员
这是为多段代码在不同的数据类型执行相同指令的情况专门设计的
声明泛型类
创建构造类型
要替代类型参数的真实类型叫做类型实参
- 泛型类声明有类型参数
- 在创建构造类型时提供的真实类型是类型实参。
创建变量和实例
class MyStack
{
int StackPointer = 0;
T[] StackArray;
public void Push(T x)
{
if (!IsStackFull)
{
StackArray[StackPointer++] = x;
}
}
public T Pop()
{
return (!IsStackEmpty)
? StackArray[--StackPointer]
: StackArray[0];
}
const int MaxStack = 10;
bool IsStackFull
{
get
{
return StackPointer >= MaxStack;
}
}
bool IsStackEmpty
{
get
{
return StackPointer <= 0;
}
}
public MyStack()
{
StackArray = new T[MaxStack];
}
public void Print()
{
for (int i = StackPointer-1; i >=0; i--)
{
Console.WriteLine("value:{0}",StackArray[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
var stackInt = new MyStack();
var stackString = new MyStack();
stackInt.Push(3);
stackInt.Push(5);
stackInt.Push(7);
stackInt.Print();
stackString.Push("Generics are great!");
stackString.Push("Hi there!");
stackString.Print();
Console.ReadKey();
}
}
返回结果为:
value:7
value:5
value:3
value:Hi there!
value:Generics are great!
非泛型 | 泛型 | |
---|---|---|
源代码大小 | 更大:我们需要为每一种类型进行一个新的实现 | 更小:不管构造类型的数量有多少,我们只需要一个实现 |
可执行大小 | 无论每一个版本的栈是否会被使用。都会在编译的版本中出现 | 可执行文件中只会出现有构造类型的类型 |
写的难易度 | 易于书写 | 比较难写 |
维护的难易度 | 更容易出问题,因为所有修改需要应用到每一个可用的类型上 | 易于维护,因为只需要修改一个地方 |
类型参数的约束
要让泛型变得更有用,我们需要提供额外的信息让编译器知道参数可以接受哪些类型。这些额外的信息叫做约束。只有符合约束的实参才能用于类型参数。
Where子句
每一个有约束的类型参数有自己的where子句
where子句可以以任何次序列出,然而,where子句中的约束必须有特定的顺序:
- 最多只能有一个主约束,有则放在第一位
- 可以有人一多的interfacename约束。
-
如果存在构造函数约束,则必须放在最后。
泛型接口
输出结果为:
5
Hi There.
使用泛型接口的示例
实现同一泛型接口的两个不同接口
泛型接口的名字不会和非泛型接口的名字冲突
泛型委托
泛型方法
调用泛型方法
泛型方法示例
扩展方法和泛型类
和非泛型扩展方法一样,泛型类的扩展方法:
- 必须声明为static;
- 必须是静态类的成员;
- 第一个参数类型中必须有关键字this,后面是扩展的泛型类的名字
介绍LINQ
- LINQ代表语言集成查询
- LINQ是.NET框架的扩展,它允许我们以数据库查询的方式查询数据集合
- 允许我们从数据库,程序对象集合以及XML文档中查询数据
class Program
{
static void Main(string[] args)
{
int[] numbers = { 2, 15, 5, 25 };
IEnumerable lowNums = //定义并储存查询
from n in numbers
where n < 10
select n;
foreach (var item in lowNums)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
输出结果如下:
2
5
匿名类型
匿名类型的对象初始化器还有其它两种允许的形式:简单标识符和成员访问表达式。这两种形式叫做投影初始化器。
查询语法和方法语法
- 查询语法是声明形式的,看上去和SQL语句很相似。查询语法适应查询表达式形式书写
-
方法语法是命令形式的,他使用标准的方法调用。方法是一组叫做标准查询运算符的方法。
其中方法语法采用的是lambda表达式
LINQ中from子句和foreach语句主要的不同点
- foreach语句在遇到代码是就执行器主体,而from子句什么也不执行。它创建一个用于保存查询变量的可枚举对象,查询本身会在之后的代码中被执行或不被执行
- foreach语句明确指定集合中的项需要按照次序,从第一个到最后一个。而fromn子句只是声明性的规定了必须考虑集合中的每一个项,不规定其顺序。
join子句
let子句
let子句接受一个表达式的运算并且把它赋值给一个需要在其他运算中使用的标识符
where子句
orderby
查询中的匿名类型
group子句
异步
namespace yibu
{
delegate long MyDel(int first, int second); //声明委托类型
class Program
{
public static long Sum(int x,int y) //声明异步方法
{
Console.WriteLine(" inside sum");
Thread.Sleep(3000); //挂起三秒
return x + y;
}
static void Main(string[] args)
{
MyDel del = new MyDel(Sum); //创建委托并引用
Console.WriteLine("异步调用之前");
IAsyncResult iar = del.BeginInvoke(3, 6, null, null); //开始异步调用
Console.WriteLine("异步调用之后");
Console.WriteLine("Doing stuff");
long result = del.EndInvoke(iar); //等待结束并获取结果
Console.WriteLine(result);
Console.ReadKey();
}
}
}
输出结果为:
异步调用之前
异步调用之后
Doing stuff
inside sum
9