介绍C#编程语言的基础入门知识点,比较简单。适合于快速上手。
C#语言基础
- 基础数据类型
有哪些?各占多少存储空间?各种类型的取值范围(最大值和最小值)?
|
类型名称(关键字) |
所占字节(1字节=8位) |
最小值 |
最大值 |
|
bool |
1 |
/ |
/ |
|
char |
2 |
/ |
/ |
|
sbyte |
1 |
-128 |
127 |
|
byte |
1 |
0 |
255 |
|
short |
2 |
-32768 |
32767 |
|
ushort |
2 |
0 |
65535 |
|
int |
4 |
-2147483648 |
2147483647 |
|
uint |
4 |
0 |
4294967295 |
|
long |
8 |
-9223372036854775808 |
9223372036854775807 |
|
ulong |
8 |
0 |
18446744073709551615 |
|
float |
4 |
-3.4028235E+38 |
3.4028235E+38 |
|
double |
8 |
-1.7976931348623157E+308 |
1.7976931348623157E+308 |
|
decimal |
16 |
-79228162514264337593543950335 |
79228162514264337593543950335 |
- 掌握要点
1.基础数据类型其实是结构体类型(值类型)。
2.char用来存储单个字符,占用16位(两个字节)的内存空间。定义字符是要用单引号表示。Char只定义一个Unicode字符。Unicode字符是目前计算机中通用的字符编码,它为针对不同语言中的每个字符设定了统一的二进制编码,用于满足跨语言、跨平台的文本转换、处理的要求。
3.float和double都是浮点类型,分别是单精度和双精度类型,所占空间大小不同,表示的精度也不一样。
4.decimal 关键字表示 128 位数据类型。同浮点型相比,decimal 类型具有更高的精度和更小的范围,这使它适合于财务和货币计算。
5.float和double是近似值,而decimal是精确值。
- 代码示例
using System;
namespace 基础类型
{
internal class Program
{
static void Main(string[] args)
{
bool a1 = true;
char a2 = 'a';
sbyte a3 = 1;
byte a4 = 1;
short a5 = 1;
ushort a6 = 1;
int a7 = 1;
uint a8 = 1;
long a9 = 1;
ulong a10 = 1;
float a11 = 1;
double a12 = 1;
decimal a13 = 1;
Console.WriteLine(sizeof(bool));
Console.WriteLine(sizeof(char));
Console.WriteLine(sizeof(sbyte));
Console.WriteLine(sizeof(byte));
Console.WriteLine(sizeof(short));
Console.WriteLine(sizeof(ushort));
Console.WriteLine(sizeof(int));
Console.WriteLine(sizeof(uint));
Console.WriteLine(sizeof(long));
Console.WriteLine(sizeof(ulong));
Console.WriteLine(sizeof(float));
Console.WriteLine(sizeof(double));
Console.WriteLine(sizeof(decimal));
ShowMinValueAndMaxValue<bool>();
ShowMinValueAndMaxValue<char>();
ShowMinValueAndMaxValue<byte>();
ShowMinValueAndMaxValue<sbyte>();
ShowMinValueAndMaxValue<short>();
ShowMinValueAndMaxValue<ushort>();
ShowMinValueAndMaxValue<int>();
ShowMinValueAndMaxValue<uint>();
ShowMinValueAndMaxValue<long>();
ShowMinValueAndMaxValue<ulong>();
ShowMinValueAndMaxValue<float>();
ShowMinValueAndMaxValue<double>();
ShowMinValueAndMaxValue<decimal>();
}
private static void ShowMinValueAndMaxValue<T>()
{
Type t = typeof(T);
var propMinValue = t.GetField("MinValue");
var propMaxValue = t.GetField("MaxValue");
if (propMinValue != null && propMaxValue != null)
{
var minValue = propMinValue.GetValue(null);
var maxValue = propMaxValue.GetValue(null);
Console.WriteLine(t.FullName + ": minValue: " + minValue + " , maxValue: " + maxValue);
}
}
}
}
- 算术运算符
- 掌握要点
自增 ++
自减 --
一元加、减 + 、-
加、减、乘、除、取余 +、-、*、/、%
复合赋值 += 、-=、*=、/=、%=
对于整型类型,这些运算符(除 ++ 和 -- 运算符以外)是为 int、uint、long 和 ulong 类型定义的。
如果操作数都是其他整型类型(sbyte、byte、short、ushort ),它们的值将转换为 int 类型,
这也是一个运算的结果类型。 如果操作数是不同的整型类型或浮点类型,
它们的值将转换为最接近的包含类型(如果存在该类型)。
- 代码示例
using System;
namespace 算术运算符
{
internal class Program
{
static void Main(string[] args)
{
//1.自增自减运算符(分为前缀和后缀,前缀是先计算后取值,后缀是先取值后计算)
Console.WriteLine("---自增自减运算符---");
int a1 = 1;
Console.WriteLine(a1++);
Console.WriteLine(a1);
Console.WriteLine(++a1);
Console.WriteLine(a1);
Console.WriteLine(a1--);
Console.WriteLine(a1);
Console.WriteLine(--a1);
Console.WriteLine(a1);
//2.一元加和元减
Console.WriteLine("---一元加和一元减---");
Console.WriteLine(+5);
Console.WriteLine(-5);
Console.WriteLine(-(-5));
//3.乘法运算符 *
Console.WriteLine("---乘法运算符---");
var a2 = 5 * 2;
Console.WriteLine(a2.GetType());
Console.WriteLine(5 * 2);
var a3 = 0.2 * 0.5;
Console.WriteLine(a3.GetType());
Console.WriteLine(0.2 * 0.5);
var a4 = 0.2f * 0.5f;
Console.WriteLine(a4.GetType());
Console.WriteLine(0.2f * 0.5f);
var a5 = 0.2m * 0.5m;
Console.WriteLine(a5.GetType());
Console.WriteLine(0.2m * 0.5m);
//4.除法运算符
Console.WriteLine("除法运算符");
Console.WriteLine(13/5);
Console.WriteLine(-13/5);
Console.WriteLine(13/-5);
Console.WriteLine(-13/-5);
Console.WriteLine(13/5.0);
Console.WriteLine(13/5.0f);
Console.WriteLine(13/5.0m);
//5.取余运算符
Console.WriteLine("取余运算符");
Console.WriteLine(13 % 5);
Console.WriteLine(-13 % 5);
Console.WriteLine(13 % -5);
Console.WriteLine(-13 % -5);
Console.WriteLine(13 % 5.0);
Console.WriteLine(13 % 5.0f);
Console.WriteLine(13 % 5.0m);
//6.加法运算符
Console.WriteLine(5+4);
Console.WriteLine(5+0.5);
Console.WriteLine(5+0.5m);
//7.复合赋值运算符
int g = 5;
g += 9; //先把g加上9,再把加完后的值赋予g
Console.WriteLine(g);
g -= 4;
Console.WriteLine(g);
g *= 2;
Console.WriteLine(g);
g /= 4;
Console.WriteLine(g);
g %= 3;
Console.WriteLine(g);
//8.byte、sbyte、short、ushort类型的运算结果
Console.WriteLine("byte、sbyte、short、ushort类型的运算结果");
byte a10 = 120;
byte a11 = 1;
var a12 = a10 + a11;
Console.WriteLine(a12.GetType());
//9.运算符优先级和关联性
Console.WriteLine("运算符优先级和关联性");
Console.WriteLine(2+2*2);
Console.WriteLine((2 + 2) *2);
//10.舍入误差
Console.WriteLine("舍入误差");
var a15 = 0.1;
var a16 = 3 * a15;
Console.WriteLine(a16);
Console.WriteLine(a16 == 0.3);
var a17 = 1 / 3.0m;
var a18 = 3 * a17;
Console.WriteLine(a18);
Console.WriteLine(a18 == 1.0m);
}
}
}
- 位运算符
- 掌握要点
一元 ~(按位求补)运算符
二进制 <<(向左移位)和 >>(向右移位)移位运算符
二进制 &(逻辑 AND)、|(逻辑 OR)和 ^(逻辑异或)运算符
这些运算符是针对 int、uint、long 和 ulong 类型定义的。 如果两个操作数都是其他整数类型(sbyte、byte、short、ushort 或 char),它们的值将转换为 int 类型,这也是一个运算的结果类型。 如果操作数是不同的整数类型,它们的值将转换为最接近的包含整数类型。 有关详细信息,请参阅 C# 语言规范的数值提升部分。
&、| 和 ^ 运算符也是为 bool 类型的操作数定义的。 有关详细信息,请参阅布尔逻辑运算符。
复合运算符
复合移位赋值 <<= 、>>=
复合逻辑and赋值 &=
复合逻辑or赋值 |=
复合逻辑^赋值 ^=
位运算和移位运算永远不会导致溢出,并且不会在已检查和未检查的上下文中产生相同的结果。
运算符优先级(从高到低)
按位求补运算符 ~
移位运算符 << 和 >>
逻辑与运算符 &
逻辑异或运算符 ^
逻辑或运算符 |
复合赋值运算符
- 代码示例
using System;
namespace 位运算符
{
internal class Program
{
static void Main(string[] args)
{
//1.按位取补
Console.WriteLine("按位取补");
uint a = 0b_0000_1111_0000_1111_0000_1111_0000_1100;
uint b = ~a;
Console.WriteLine(Convert.ToString(b, 2));
//2.左移
Console.WriteLine("左移");
uint c = a << 4;
Console.WriteLine(Convert.ToString(c, 2));
Console.WriteLine("byte类型左移");
byte a1 = 0b_1111_0001;
var b2 = a1 << 8;
Console.WriteLine(b2.GetType());
Console.WriteLine(Convert.ToString(b2, 2));
//3.右移
Console.WriteLine("右移");
uint x = 0b_1001;
uint y = x>> 2;
Console.WriteLine(Convert.ToString(y, 2));
//如果左侧操作数类型是uint或ulong,则右移运算符执行逻辑移位:高
//顺序为位位置始终设置为零。
uint z = 0b_1000_0000_0000_0000_0000_0000_0000_0000;
uint z1 = z >> 3;
Console.WriteLine(Convert.ToString(z1, 2));
//4.逻辑and
Console.WriteLine("逻辑and");
uint a3 = 0b_1111_1000;
uint b3 = 0b_1001_1101;
uint c3 = a3 & b3;
Console.WriteLine(Convert.ToString(c3, 2));
//5.逻辑or
Console.WriteLine("逻辑or");
uint a4 = 0b_1111_1000;
uint b4 = 0b_1001_1101;
uint c4 = a4 | b4;
Console.WriteLine(Convert.ToString(c4, 2));
//6.逻辑异或
Console.WriteLine("逻辑异或");
uint a5 = 0b_1111_1000;
uint b5 = 0b_1001_1101;
uint c5 = a5 ^ b5;
Console.WriteLine(Convert.ToString(c5, 2));
//7.运算符的优先级
Console.WriteLine("运算符的优先级");
a = 0b_1;
var f = 9;
a >>= 1 & 1;
Console.WriteLine(a);
a <<= 2;
a |= b;
a &= b;
a ^= b;
Console.WriteLine(a);
}
}
}
- 布尔运算符
- 掌握要点
以下运算符使用 bool 操作数执行逻辑运算:
一元 !(逻辑非)运算符。
逻辑非支持可空布尔类型。
二元 &(逻辑与)、|(逻辑或)和 ^(逻辑异或)运算符。 这些运算符始终计算两个操作数。
|
x |
y |
x|y(或) |
x&y(与) |
x^y(异或) |
|
true |
true |
true |
true |
false |
|
true |
false |
true |
false |
true |
|
true |
null |
true |
null |
null |
|
false |
true |
true |
false |
true |
|
false |
false |
false |
false |
false |
|
false |
null |
null |
false |
null |
|
null |
true |
true |
null |
null |
|
null |
false |
null |
false |
null |
|
null |
null |
null |
null |
null |
总结一下计算操作数其中一个为null的规律:
1.运算符为或|:true > null > false
2.运算符为与&:false > null > true
3.运算符为异或^:null > (true、false)
另外,
如果两个操作数为false或true,&相当于&&,|相当于||
二元 &&(条件逻辑与)和 ||(条件逻辑或)运算符。 这些运算符仅在必要时才计算右侧操作数。
但是条件逻辑与,条件逻辑或不支持可空布尔类型。
对于整型数值类型的操作数,&、| 和 ^ 运算符执行位逻辑运算。
运算符优先级:
逻辑非运算符 !
逻辑与运算符 &
逻辑异或运算符 ^
逻辑或运算符 |
条件逻辑与运算符 &&
条件逻辑或运算符 ||
- 代码示例
using System;
namespace 布尔运算符
{
internal class Program
{
static void Main(string[] args)
{
//逻辑非 !
bool? a = null;
var b1 = !a;
a = true;
var b2 = !a;
a = false;
var b3 = !a;
Console.WriteLine("------------逻辑或-----------");
Console.WriteLine(b1);
Console.WriteLine(b2);
Console.WriteLine(b3);
bool? a2 = null;
bool? c2 = true;
bool? d2 = false;
Console.WriteLine(a2 | c2);
Console.WriteLine(a2 | d2);
Console.WriteLine(c2 | d2);
Console.WriteLine("------------逻辑与-----------");
Console.WriteLine(a2 & c2);
Console.WriteLine(a2 & d2);
Console.WriteLine(c2 & d2);
Console.WriteLine("------------逻辑异或-----------");
Console.WriteLine(a2 ^ c2);
Console.WriteLine(a2 ^ d2);
Console.WriteLine(c2 ^ d2);
//Console.WriteLine(c2 || d2);//不支持
Console.WriteLine("运算符优先级");
var e = !false && (false & true) || (true ^ true) && (false | true);
// (true && false) || (false && true)
// false || false
// false
Console.WriteLine(e);
}
}
}
- 关系运算符
- 掌握要点
<(小于)、>(大于)、<=(小于或等于)和 >=(大于或等于),==(等于)
所有整型和浮点数值类型都支持这些运算符。
- 代码示例
using System;
namespace 关系运算符
{
internal class Program
{
static void Main(string[] args)
{
//小于 <
Console.WriteLine("小于");
Console.WriteLine(0.0 < 9.0);
Console.WriteLine(9.0 < 9.0);
Console.WriteLine(10.0 < 9.0);
//小于等于 <=
Console.WriteLine("小于等于");
Console.WriteLine(0.0 <= 9.0);
Console.WriteLine(9.0 <= 9.0);
Console.WriteLine(10.0 <= 9.0);
//大于 >
Console.WriteLine("大于");
Console.WriteLine(0.0 > 9.0);
Console.WriteLine(9.0 > 9.0);
Console.WriteLine(10.0 > 9.0);
//大于等于 >=
Console.WriteLine("大于等于");
Console.WriteLine(0.0 >= 9.0);
Console.WriteLine(9.0 >= 9.0);
Console.WriteLine(10.0 >= 9.0);
//数值类型相等运算符
Console.WriteLine("数值类型相等运算符");
int a = 1 + 2 + 3;
int b = 6;
Console.WriteLine(a == b);
//字符串类型相等运算符
Console.WriteLine("字符串类型相等运算符");
string s1 = "hello";
string s2 = "hello";//s1和s2是同一个对象
Console.WriteLine(s1 == s2);
Console.WriteLine(Object.ReferenceEquals(s1,s2));//所以对象相同
//委托相等
Console.WriteLine("委托相等");
Action a3 = () =>
{
Console.WriteLine("a");
//Console.WriteLine("1");
};
Action b3 = () =>
{
Console.WriteLine("a");
//Console.WriteLine("1");
};
Action c3 = a3;
Console.WriteLine(a3 == b3);
Console.WriteLine(Object.ReferenceEquals(a3, b3));//所以对象相同
Console.WriteLine(c3 == b3);
Console.WriteLine(Object.ReferenceEquals(c3, b3));//所以对象相同
//不等于!=
}
}
}
- 语句
- 掌握要点
声明语句
常量声明语句
表达式语句
方法调用语句
创建对象语句
if
while
do while
for
foreach
continue
break
goto
return
yield return
try catch finally throw
空语句
嵌套语句块
无法访问的语句
- 代码示例
using System;
using System.Collections.Generic;
namespace _06.语句
{
internal class Program
{
static void Main(string[] args)
{
//1.声明语句
double a;
//2.常量声明语句
const double b = 1.0;
//3.表示式语句
double c = 1.0 * 2 - 10;
//4.方法调用语句
Console.WriteLine();
//5.创建对象语句
List<int> d = new List<int>();
//6.条件语句
if (10 > 9)
{
Console.WriteLine("yes");
}
//7.while
int j = 0;
while (j < 10)
{
j++;
}
//8.do while
int k = 0;
do //至少执行一次方法体
{
k++;
}
while (k < 10);
//9.for
for (int i = 1; i < 10; i++)
{
Console.WriteLine(i);
}
//10.foreach(编译可枚举类型对象)
foreach (var dItem in d)
{
Console.WriteLine(dItem);
}
//11.continue
for (int i = 0; i < 10; i++)
{
if (i > 5) continue;
Console.WriteLine(i);
}
//12.break
while (true)
{
if (1 > 0)
{
break;
}
}
//13.goto
//goto End;
//14.return
getAInt();
//15.yield return
getArrayNumber();
//16.try catch finally throw
try
{
throw new Exception("自定义错误消息。。。");
}
catch (Exception e)
{
Console.WriteLine(e.Message + e.StackTrace);
}
finally
{
Console.WriteLine("finally 一定会执行");
}
//17.空语句
int g = 0;
while (g++ < 100)
{
;//空语句,不作任何操作
}
//18.嵌套语句
for (int i = 0; i < 100; i++)
{
for (int h = 0; h < 100; h++)
{
;
}
}
//19.无法访问的语句
if (9 > 10)
{
Console.WriteLine("不可能");
}
End:
Console.WriteLine("这个main方法结束了...");
}
public static int getAInt()
{
return 10;
}
public static IEnumerable<int> getArrayNumber()
{
for (int i = 1; i <= 3; i++)
{
yield return i;//返回一个可枚举int类型对象[1,2,3]
}
}
}
}
- 类
- 掌握要点
c#是一种面向对象的编程语言,类是代表一类对象的统称,对象是类的具体实例。
类对象可以声明,不引用实例(但是不建议这么做,因为运行时访问可能失败)。
class是引用类型,可以通过new关键字创建实例或赋值为兼容类型(同类或子类)的实例。
public是访问修饰语,表示可以被公开的创建实例,还有其他的修改语:如private(私有的)、
protected(受保护的)、internal(本程序集内部)
继承是面向对象的基本特性,但是只能继承单个类(类的继承链)。
抽象类是由修饰符“abstract”修饰,
包含“abstarct”修饰的抽象方法(只有声明,没有实现),
被子类继承后必须实现方法体。
抽象类不能被实例化。
密封类,不能被继承的类。
- 代码示例
using System;
namespace _07.类
{
public class Program
{
static void Main(string[] args)
{
//Animal animal = new Animal();
//animal.Walk();
Animal person = new Person(); //基类变量用子类来实例化
person.Walk();//动态绑定子类重写的方法
Animal chinese = new Chinese(); //基类变量用子类来实例化
chinese.Walk();//动态绑定子类重写的方法
}
}
public abstract class Animal
{
public abstract void Walk();// { Console.WriteLine("Animal walk"); }
}
public class Person : Animal//,A C#b不支持多重继承
{
public override void Walk() { Console.WriteLine("Person walk"); }
}
public class Chinese : Person
{
public override void Walk() { Console.WriteLine("Chinese walk"); }
}
//密封类不能被继承
public sealed class A
{
}
//public class B : A { }
}
- 结构体
- 掌握要点
结构是由struct关键字修饰的“值类型”,结构类型具有值语义 。
也就是说,结构类型的变量包含类型的实例(变量自包含)。
由于结构类型具有值语义,因此建议定义不可变的 结构类型。
从 C# 7.2 开始,可以使用 readonly 修饰符来声明结构类型为不可变。
readonly 结构的所有数据成员都必须是只读的
结构类型的设计限制:
1.不能声明无参数构造函数。
2.不能在声明实例字段或属性时对它们进行初始化。
3.结构类型的构造函数必须初始化该类型的所有实例字段。
4.结构类型不能从其他类或结构类型继承,也不能作为类的基础类型。但是,结构类型可以实现接口。
5.不能在结构类型中声明终结器(析构方法)。
在 C# 中,必须先初始化已声明的变量,然后才能使用该变量。
由于结构类型变量不能为 null(除非它是可为空的值类型的变量),
因此,必须实例化相应类型的实例。 有多种方法可实现此目的。
通常,可使用 new 运算符调用适当的构造函数来实例化结构类型。
每个结构类型都至少有一个构造函数。 这是一个隐式无参数构造函数,用于生成类型的默认值。
还可以使用默认值表达式来生成类型的默认值。
如果结构类型的所有实例字段都是可访问的,则还可以在不使用 new 运算符的情况下对其进行实例化。
在这种情况下,在首次使用实例之前必须初始化所有实例字段。
按引用传递结构类型变量
将结构类型变量作为参数传递给方法或从方法返回结构类型值时,
将复制结构类型的整个实例。
这可能会影响高性能方案中涉及大型结构类型的代码的性能。
通过按引用传递结构类型变量,可以避免值复制操作。
- 代码示例
using System;
namespace _08.结构体
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//值类型不能初始化为null,除非是可空类型
//MyStruct? myStruct = null;
MyStruct myStruct2 = new MyStruct(10,20);
myStruct2.ShowAllMemberValue();
Transfer(myStruct2);
myStruct2.ShowAllMemberValue();
}
public static void Transfer(MyStruct s)
{
s.B = "abcd";
}
}
public struct MyStruct : IClass1//: MyClass1//: MyStruct1
{
//1.约束1:不能声明无参的构造方法
//public MyStruct() { }
//2.约束2:不能在声明字段和属性的时候初始化
private int a;
private int b;
public string B { get; set; }
//3.约束:在构造方法里面必须对所有的字段和属性进行初始化
public MyStruct(int _a, int _b)
{
a = _a;
b = _b;
B = "";
}
public void ShowAllMemberValue()
{
Console.WriteLine("a:" + a + ", b:" + b + ", B:" + B);
}
//约束5:结构体不能包含析构方法
//public ~MyStruct() { }
}
public struct MyStruct1
{
}
//4.约束4:结构体不能作为基类被继承
public class MyClass1 //: MyStruct
{
}
public interface IClass1 { }
}
- 接口
- 掌握要点
接口包含了一组相关的功能性(字段、属性、事件、索引器、操作符),可被非抽象类和struct实现。类可以实现多个接口。所以C#是一门单一类继承链,但是可以实现多接口的语言(类似java)。
接口不能包含字段,自动属性,和类似属性的事件,
使用接口的好处是可以实现多个不同接口(弥补不能多继承),
为结构体实现接口
接口可以包含实例方法,属性,事件和索引器(以及这4种的组合),静态构造方法,静态字段、静态常量和静态操作符。
不可以包含字段,实例构造方法,和实例析构方法。成员能被访问修改符修饰,private的成员必须有默认实现。
实例方法(如果是私有的必须有默认实现)
事件只能被自身的实例方法调用。
- 代码示例
using System;
namespace _09.接口
{
internal class Program
{
static void Main(string[] args)
{
MyInterface i = new MyClass();
//事件绑定方法
i.PropertyChanged += (sender, e) => { Console.WriteLine(e); };
i.PropertyChanged += (sender, e) => { Console.WriteLine("abcd"); };
i.Hello2();
MyInterface i2 = i[100];
MyInterface i3 = i[0];
Console.WriteLine("i2 == i"+ (i2 == i));
Console.WriteLine("i3 == i"+ (i3 == i));
}
}
public interface MyInterface
{
//不能包含实例构造方法,即接口本身不能实例化
//public MyInterface() { }
//不能包含实例字段
//private int a;
//不支持实例委托
//public Action Action;
//不支持析构方法
//public ~MyInterface() { }
//可以包含属性
public string B { get; set; }
//支持事件
public event EventHandler<int> PropertyChanged;
//支持普通方法
public void Hello() { }
//支持抽象方法
public abstract void Hello2();
//支持虚方法
public virtual void Hello3() { }
//支持定义索引器
public MyInterface this[int index] { get;set; }
//支持静态构造方法
static MyInterface()
{
}
//支持静态字段
public static string _a;
//支持静态属性
public static string _B { get; set; }
//静态常量
public const string _C = "hello";
public static string operator +(MyInterface left, MyInterface right)
{
return left.B + right.B;
}
}
public class MyClass : MyInterface
{
public MyInterface this[int index] { get{ if (index > 0) return this; else return null; } set { } }
public string B { get; set; }
public event EventHandler<int> PropertyChanged;
public void Hello2()
{
PropertyChanged?.Invoke(this, 10);
}
}
}
- 委托和事件
- 掌握要点
委托可以理解为对一个方法原型的定义(可以理解为一种特殊类的定义),原型里面有委托的名称,委托的返回类型,参数列表。泛型委托则是增加了泛型参数的委托。
委托原型用delegate关键字修饰。
Public delegate void MyDelegate();//普通的委托
Public delegate T GenericDelegate(T a);//泛型委托
事件则是一些预先声明好的委托,形如:
public delegate void EventHandler(object? sender, EventArgs e);
的一个成员定义,通常作为类成员定义。事件用event关键字修饰。
public class MyEventSource
{
public event EventHandler eventHandler;
}
委托的使用需要定义一个委托的变量,然后为委托变量赋值,这个值是实际的一个方法定义(可以为匿名方法、类方法或Lambda表达式(可理解为方法的简写))。
委托可以像类一样在命名空间里面定义,事件则不能。类里面能定义委托成员和事件成员,在方法上下文里面也可以定义委托变量和事件变量。
- 代码示例
using System;
namespace _10.委托和事件
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//赋值为一个匿名方法
MyDelegate d1 = delegate { Console.WriteLine("我是匿名方法,被调用了"); };
//连接一个类方法,+=为多播委托
d1 += Func;
//连接一个lambda表达式
d1 += () => { Console.WriteLine("我是lambda表达式,被调用了"); };
d1();//调用
GenericDelegate<int> g = (a) => { return a + 10; };
g += (a) => { return a + 100; };
var c = g(10);
Console.WriteLine("c:" + c);// 如果是多播委托,这个委托有返回类型参数,这返回结果是最后一个绑定方法的返回值
MyEventSource myEventSource = new MyEventSource();
//绑定事件源的事件处理器
EventHandler<int> ev = (s, o) =>
{
Console.WriteLine(o);
};
myEventSource.eventHandler += ev;
//事件不能在外部上下文直接调用,而只能在外部绑定或解绑处理器
//myEventSource.eventHandler(new object(), new EventArgs() );
myEventSource.CallEvent();
myEventSource.eventHandler -= ev;
myEventSource.CallEvent();
}
public static void Func()
{
Console.WriteLine("我是类方法,被调用了");
}
}
public delegate void MyDelegate();
public delegate int MyDelegate1(int a);
public delegate T GenericDelegate<T>(T a);
public class MyEventSource
{
public event EventHandler<int> eventHandler;
public void CallEvent()
{
eventHandler?.Invoke(this, 1);
}
}
}
- 属性
- 掌握要点
属性是类或对象中的一种智能字段形式。 从对象外部,它们看起来像对象中的字段。
但是,属性可以通过丰富的 C# 功能来实现。
你可以提供验证、不同的可访问性、迟缓计算或方案所需的任何要求。
属性有哪些?
自动属性
自定义存储的属性
属性实现是单个表达式(简写)
带验证的属性
只读属性
只读属性,构造器初始化
初始化作为只读属性公开的集合
计算属性
缓存的计算属性
将特性附加到自动实现的属性
实现 INotifyPropertyChanged( PropertyChangedEventHandler)的属性
- 代码示例
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace _11.属性
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Person01 person01 = new Person01();
person01.FirstName = "hello";
Console.WriteLine(person01.FirstName);
}
}
public class Person01
{
public string FirstName { get; set; } //这种写法叫自动属性
}
public class Person02
{
//以下的写法叫做自定义存储的属性
private string _firstName;
public string FirstName
{
set
{
_firstName = value;
}
get
{
return _firstName;
}
}
}
public class Person03
{
//以下的写法叫做自定义存储的属性(属性的实现是单个表达式)
private string _firstName;
public string FirstName
{
set =>_firstName = value;
get =>_firstName;
}
}
public class Person04
{
private string _firstName;
public string FirstName
{
set
{
//带验证的属性
if (string.IsNullOrWhiteSpace(value))
{
throw new Exception("给这个字段赋值不能为空值");
}
_firstName = value;
}
get
{
return _firstName;
}
}
}
public class Person05
{
//只读属性
public string FirstName { get; private set; } = string.Empty;
public void SetName()
{
FirstName = "jak";
}
}
public class Person06
{
//只读属性
public string FirstName { get; private set; }
//在构造器里面初始化
public Person06() => FirstName = string.Empty;
}
//只读集合属性
public class MyPropClass
{
public ICollection<int> ListInt { get; } = new List<int>();
}
public class Person07
{
public string FirstName { get; set; }
public string LastName { get; set; }
//计算属性
public string FullName
{
get { return FirstName + " " + LastName; }
}
}
public class Person08
{
private string _fullName;
[Key]
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
//带缓冲的属性
public string FullName
{
get
{
if (_fullName == null)
{
_fullName = FirstName + " " + LastName;
}
return _fullName;
}
}
}
public class Person09 : INotifyPropertyChanged
{
private string _firstName;
public string FirstName {
get
{
return _firstName;
}
set
{
//属性变化事件触发器
if (_firstName != value)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FirstName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
- 逆变和协变
- 掌握要点
通过一个例子来说明:
泛型参数T1用in修饰,表示是输入参数;TResult用out修饰,表示结果参数。
public delegate TResult Func1<in T1, out TResult>(T1 arg);
//父类
public class A
{
}
//子类B
public class B : A
{
}
//子类C
public class C : B
{
}
Func1<A, B> abc = delegate (A arg) {C c = new C(); return c; };
//这个委托赋值的意思是:
//输入参数必须为B类型或它的派生类型,但他们的父类都为A(这个过程叫做逆变),
//输出参数为B类型或它的派生类型,但它们的父类都为A(这个过程叫做协变)。
Func1<B, A> abcd = abc;
abcd(new B());
- 代码示例
using System;
namespace _12.逆变和协变02
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Func1<A, B> abc = delegate (A arg) { D c = new D(); return c; };
//输入参数必须为B类型或它的派生类型,但它们的父类都为A(这个过程叫做逆变),
//输出参数为B类型或它的派生类型,但它们的父类都为A(这个过程叫做协变)。
Func1<B, A> abcd = abc;
abcd(new B());
abcd(new C());
abcd(new D());
}
}
//父类
public class A
{
}
//子类B
public class B : A
{
}
//子类C
public class C : B
{
}
//子类C
public class D : C
{
}
//泛型参数T1用in修饰,表示是输入参数;TResult用out修饰,表示结果参数。
public delegate TResult Func1<in T1, out TResult>(T1 arg);
}
- 泛型
- 掌握要点
泛型允许根据不同的类型参数设计类,方法,接口,委托。
泛型的作用的可重用性,类型安全性。
泛型集合类是C#常见的内置类型。
- 代码示例
using System;
using System.Collections.Generic;
namespace _13.泛型
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var t = new Test<int>();
t.Value = 10;
var t2 = new Test<string>();
t2.Value = "abc";
Test(new List<int>() { 1 });
Test(new List<string>() { "abc","efg" });
Test(new List<MyTest>() { new MyTest(), new MyTest()});
Test1<int> test1 = (arg) => { Console.WriteLine(arg); };
}
//这个是一个简单的泛型方法
static void Test<T>(IList<T> args)
{
foreach (T arg in args)
{
Console.Write(arg);
Console.Write(",");
}
Console.WriteLine();
}
}
public class MyTest
{
}
//这是一个简单的泛型类,最大的特点就是可重用性和类型安全性
public class Test<T>
{
public T Value { get; set; }
}
//泛型委托
public delegate void Test1<T>(T arg);
//泛型接口
public interface A<T>
{
public T Get(T arg);
}
//继承一个泛型接口
public class AA : A<int>
{
public int Get(int arg)
{
return arg + 10;
}
}
}
- 迭代器
- 掌握要点
迭代器可用于逐步迭代集合,例如列表和数组。
迭代器方法或 get 访问器可对集合执行自定义迭代。
迭代器方法使用 yield return 语句返回元素,每次返回一个。
到达 yield return 语句时,会记住当前在代码中的位置。
下次调用迭代器函数时,将从该位置重新开始执行。
通过 foreach 语句或 LINQ 查询从客户端代码中使用迭代器。
直至到达第一个 yield return 语句。 此迭代返回的值为 3,
并保留当前在迭代器方法中的位置。 在循环的下次迭代中,
迭代器方法的执行将从其暂停的位置继续,
直至到达 yield return 语句后才会停止。
此迭代返回的值为 5,并再次保留当前在迭代器方法中的位置。
到达迭代器方法的结尾时,循环便已完成。
迭代器方法或 get 访问器的返回类型可以是 IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>。
- 代码示例
using System;
using System.Collections;
using System.Collections.Generic;
namespace _14.迭代器
{
internal class Program
{
static void Main(string[] args)
{
var coll = GetSomeNumbers();
foreach (var item in coll)
{
Console.WriteLine(item);
}
var col2 = GetDays();
foreach (var item in col2)
{
Console.WriteLine(item);
}
var stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
foreach (var item in stack)
{
Console.WriteLine(item);
}
}
public static IEnumerable GetSomeNumbers()
{
yield return 1;
yield return 3;
yield return 5;
}
public static IEnumerable GetDays()
{
string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
for (var i = 0; i < days.Length; i++)
{
yield return days[i];
}
}
}
public class Stack<T> : IEnumerable<T>
{
private T[] _Array = new T[100];
private int pos = 0;
public void Push(T val)
{
if (pos >= 100)
{
throw new Exception("超过最大容量!");
}
_Array[pos++] = val;
}
public T Pop()
{
if (pos <= 0)
{
throw new Exception("栈已没有元素了!");
}
return _Array[--pos];
}
public IEnumerator<T> GetEnumerator()
{
for (var i = pos - 1; i >= 0; i--)
{
yield return _Array[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
更多推荐

所有评论(0)