0%

CSharp基础

记录写bug过程中遇到的一些疑问

面向对象

protected:只有同一类或派生类中的代码可以访问该类型或成员

internal:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。

protected internal:同一程序集中的任何代码或其他程序集中的任何派生类都可以访问该类型或成员。

private protected:同一类或基类程序集内派生类中的代码可以访问该类型或成
员。

abstract:要求在派生类中重写类成员。

数据类型

值类型存储在堆栈上,引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。
C#类为引用类型,结构为值类型。
从值类型转化为引用类型称为装箱,如果方法需要把一个对象作为参数,而且传送一个值类型,装箱操作会自动进行;装箱的值类型可以使用拆箱操作转换为值类型,在拆箱时,需要使用类型转换运算符

值类型

结构体 数值类型(整数,浮点数,高精度浮点数) 可空类型 布尔 字符 枚举

引用类型

数组,用户定义的类、接口、委托,object,字符串

装箱和拆箱

装箱是一种通过将变量存储到System.Object中来显式地将值类型转换为引用类型的机制。当您装入值时,CLR会将新对象分配到堆中,并将值类型的值复制到该实例中。例如:

1
2
int a = 20;  
object b = a; //装箱

相反的操作是拆箱,它是将引用类型转换回值类型的过程。此过程验证接收数据类型是否与装箱类型一致;
int c = (int)b; // 拆箱

泛型

特征:

类型安全
性能
二进制代码复用

详见: 泛型的特点

为什么不用object代替泛型

由于Object为所有类型的基类,所以可以处理任何数据类型的数据,但是其中存在这拆箱和装箱,如果数据太多会影响到程序的性能。
在使用泛型的时候程序会在编译阶段根据我们提供的类型生成相应的二进制代码,无须进行装箱和拆箱操作。

接口

为什么要用接口

接口一般由上层人员发起,下层人员实现。
写接口并不是为了扩展,而是为了扩展以后的模块仍然跟项目模块保持高度一致,为了扩展后的规范化。

反射

实例化接口对象

接口回调

接口不仅可以声明对象,而且可以把对象实例化,还可以当做参数被传入。
即继承中的向上转型,父类 FL=new 子类(),只不过这里的父类就是interface接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Itemp
{
double plus();
}

public class num : Itemp
{
double aa, bb;
public num(double a, double b)
{
this.bb = b;
this.aa = a;
}
public double plus()
{
return (aa + bb);
}
}

static void Main(string[] args)
{
Itemp tm = null;//声明接口对象引用
tm = new num(1, 2);//接口回调(向上转型)
Console.WriteLine(tm.plus());
}

类型参数约束

  在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。
类型参数约束.NET支持的类型参数约束有以下五种:

1
2
3
4
5
6
where T : struct  //类型参数必须是值类型;可以指定除 Nullable 以外的任何值类型
where T : class //类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型
where T : new() //类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定
where T : NameOfBaseClass //类型参数必须是指定的基类或派生自指定的基类
where T : NameOfInterface //类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的
where T : U //为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数

当前程序运行路径

Directory.GetCurrentDirectory() + "\\Config

数据类型补充

@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
double d = 3D;
d = 4d;
d = 3.934_001;
float f = 3_000.5F;
f = 5.4f;
decimal myMoney = 3_000.5m;
myMoney = 400.75M;

[Flags]
public enum Days
{
None = 0b_0000_0000, // 0
Monday = 0b_0000_0001, // 1
Tuesday = 0b_0000_0010, // 2
Wednesday = 0b_0000_0100, // 4
Thursday = 0b_0000_1000, // 8
Friday = 0b_0001_0000, // 16
Saturday = 0b_0010_0000, // 32
Sunday = 0b_0100_0000, // 64
Weekend = Saturday | Sunday
}
Days meetingDays = Days.Monday | Days.Wednesday | Days.Friday;

checked 关键字用于对整型类型算术运算和转换显式启用溢出检查

1
2
3
4
5
6
7
8
int i = 2147483647;
checked
{
i += 10;
Console.WriteLine(i);
}

//i=0;不会抛异常

lock

lock 语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lock。

lock(引用类型)

当同步对共享资源的线程访问时,请锁定专用对象实例(例如,private readonly object balanceLock = new object(); )或另一个不太可能被代码无关部分用作 lock 对象的实例。避免对不同的共享资源使用相同的 lock 对象实例,因为这可能导致死锁或锁争用

when

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var client = new HttpClient();
var streamTask = client.GetStringAsync("https://localHost:10000");
try
{
var responseText = await streamTask;
return responseText;
}
catch (HttpRequestException e) when (e.Message.Contains("301"))
{
return "Site Moved";
}
catch (HttpRequestException e) when (e.Message.Contains("404"))
{
return "Page Not Found";
}
catch (HttpRequestException e)
{
return e.Message;
}

case 标签无需是互斥的,且 case 标签在 switch 语句中的显示顺序可以决定要执行的 switch块。 when 关键字可指定一个筛选条件,该条件使得仅当筛选条件也为 true 时,与其相关联的 case 标签才为 true。

1
2
3
4
5
6
case (expr) when (when-condition):

case Shape shape when shape.Area == 0:
Console.WriteLine($"The shape: {shape.GetType().Name} with no dimensions");
break;

运算符 C#8.0

从末尾运算符 ^ 开始索引

1
2
3
int[] xs = new[] { 0, 10, 20, 30, 40 };
int last = xs[^1];
// last = 40
1
2
3
4
5
6
7
8
9
10
int[] numbers = new[] { 0, 10, 20, 30, 40, 50 };
int start = 1;
int amountToTake = 3;
int[] subset = numbers[start..(start + amountToTake)];
Display(subset); // output: 10 20 30

//a.. 等效于 a..^0
//..b 等效于 0..b
//.. 等效于 0..^0

命名空间别名限定符 :: 访问已设置别名的命名空间的成员

1
2
3
4
5
6
using forwinforms = System.Drawing;
using forwpf = System.Windows;
public class Converters
{
public static forwpf::Point Convert(forwinforms::Point point) => new forwpf::Point(point.X, point.Y);
}