抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

可空类型

概念

在一个类型后面加上问号”?”表示可空类型

例如 int? a 表示a可以是一个数字,也可以是null

转换

对于非空的情况,可以添加显式转换

1
2
3
int? a = 10;
int b = (int)a;
Console.WriteLine(b);

但是当a为null时会报错,因此需要加上if语句

1
2
3
4
5
6
7
int? a = null;
int b =
a == null
? -1
: (int)a;
Console.WriteLine(b);
//输出: -1

扩展方法

概念

扩展方法被定义在非泛型静态类中,扩展方法能够为现有的类添加新的方法,而无需定义新的类

示例

幂运算需要用到Math.Pow()函数,通过扩展方法,可以在int类型中添加Pow()方法,更快捷地计算幂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
static void Main(string[] args)
{
int a = 2;
//计算2的10次方
Console.WriteLine(a.Pow(10));
Console.ReadKey();
}
}

public static class Extend
{
public static int Pow(this int num, int value)
{
return (int)Math.Pow(num, value);
}
}

序列化对象的二进制储存

通过将一个类序列化,可以用二进制的方式在硬盘上保存这个类

1
2
3
4
5
6
7
[Serializable]
class Struct
{
public int a = 10;
public string b = "123";
public Object c;
}

如果对象中出现对其它对象的引用,那么被引用的对象也会被写入硬盘里,在下次读取时仍然可用

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
static void Main(string[] args)
{
Struct s = new Struct()
{
a = 99,
b = "DearXuan",
c = new Struct()
};
//保存
using(FileStream fileStream1 = new FileStream(@"D:\1.xuan",FileMode.OpenOrCreate))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(fileStream1, s);
}
//读取
using (FileStream fileStream = new FileStream(@"D:\1.xuan", FileMode.OpenOrCreate))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
Struct ss = binaryFormatter.Deserialize(fileStream) as Struct;
Console.WriteLine(ss.a);
Console.WriteLine(ss.b);
Console.WriteLine(((Struct)ss.c).a);
}
Console.ReadLine();
}

由于数据是二进制的形式储存,因此文件后缀名可以任意取

UWP的UI线程

UI线程

UI线程维护一个消息队列,所有的UI事件都会被送入消息队列中,在UI线程里执行。如果UI线程中存在耗时操作,就会导致消息得不到及时处理,程序无法响应输入,出现界面卡死

异步任务

使用async修饰方法,使之成为异步任务,用await修饰语句,使之成为等待任务

await修饰的代码将会在子线程中执行,并且不会有返回值

下面的代码生成了一个弹窗,使用await修饰ShowAsync(),使之不会阻塞UI线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public async static void ShowOKDialog(string title, string content, Action onOkClick, Action onCloseClick)
{
ContentDialog dialog = new ContentDialog();
dialog.Title = title;
dialog.Content = content;
dialog.PrimaryButtonText = "好的";
dialog.CloseButtonText = "取消";
dialog.DefaultButton = ContentDialogButton.Primary;
if(onOkClick != null)
{
dialog.PrimaryButtonClick += (_s, _e) => { onOkClick(); };
}
if(onCloseClick != null)
{
dialog.CloseButtonClick += (_s, _e) => { onCloseClick(); };
}
await dialog.ShowAsync();
}

想要对用户的点击事件做出响应,只需要为“确定”和“取消”按钮添加点击事件即可

跨线程更新UI

使用以下代码将函数放在UI线程执行。如果涉及UI更新的函数在子线程中执行则会报错

1
2
3
4
public async static void Invoke(Action action, CoreDispatcherPriority Priority = CoreDispatcherPriority.Normal)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Priority, () => { action(); });
}

默认参数

使用默认参数

直接在方法的参数里为变量赋值,其值会作为默认值传入

1
2
3
4
public static int add(int a = 5,int b = 10,int c = 15)
{
return a + b + c;
}

此时调用add(),会返回30

1
2
3
4
5
6
static void Main(string[] args)
{
Console.Write(add());
//结果: 30
Console.ReadLine();
}

覆盖默认参数

按顺序在add()中输入参数,默认参数将会被覆盖

1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args)
{
Console.Write(add(0,0));
//结果: 15
Console.ReadLine();
}

public static int add(int a = 5,int b = 10,int c = 15)
{
return a + b + c;
}

上面的代码在调用add()时输入了两个参数,但是add()有三个参数,因此前两个被覆盖了

如果希望不按顺序,只需要在参数前面加上变量名

1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args)
{
Console.Write(add(a: 0, c: 0));
//结果: 10
Console.ReadLine();
}

public static int add(int a = 5,int b = 10,int c = 15)
{
return a + b + c;
}

上面的代码指定了a和c的变量值为0,而b仍为默认值,因此输出结果10

自动释放资源

IDispose接口

在using语句中定义的对象,将会在脱离using语句后自动释放资源

IDispose接口提供了一种方法来让程序自动释放资源,你需要把释放资源的语句写在Dispose()函数中

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
26
27
class Program
{
static void Main(string[] args)
{
using(Example example = new Example())
{
Console.WriteLine("1");
}
// 运行结果:
// Create
// 1
// Dispose
}
}

class Example: IDisposable
{
public Example()
{
Console.WriteLine("Create");
}

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

在读取文件时,将FileStream定义在using语句中,可以在执行完毕后自动释放,以免长时间占用

1
2
3
4
using(FileStream fileStream = new FileStream(@"D:\1.xuan",FileMode.OpenOrCreate))
{
//读取文件
}

析构函数

析构函数与构造函数相反,析构函数在对象被gc释放时调用,因此你无法控制它被调用的具体时间

析构函数中不应该出现任何耗时操作或死循环,否则函数将会被系统强行中断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Program
{
static void Main(string[] args)
{
Example example = new Example();
example = null;
}
// 运行结果:
// Create
// Dispose
}

class Example
{
public Example()
{
Console.WriteLine("Create");
}

~Example()
{
Console.WriteLine("Dispose");
}
}

评论