C# 编程技巧

2022-01-19 17:58:59 浏览数 (1)

可空类型

概念

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

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

转换

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

代码语言:javascript复制
int? a = 10;
int b = (int)a;
Console.WriteLine(b);

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

代码语言:javascript复制
int? a = null;
int b = 
    a == null 
    ? -1 
    : (int)a;
Console.WriteLine(b);
//输出: -1

扩展方法

概念

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

示例

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

代码语言:javascript复制
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);
    }
}

序列化对象的二进制储存

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

代码语言:javascript复制
[Serializable]
class Struct
{
    public int a = 10;
    public string b = "123";
    public Object c;
}

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

代码语言:javascript复制
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线程

代码语言:javascript复制
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更新的函数在子线程中执行则会报错

代码语言:javascript复制
public async static void Invoke(Action action, CoreDispatcherPriority Priority = CoreDispatcherPriority.Normal)
{
    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Priority, () => { action(); });
}

默认参数

使用默认参数

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

代码语言:javascript复制
public static int add(int a = 5,int b = 10,int c = 15)
{
    return a   b   c;
}

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

代码语言:javascript复制
static void Main(string[] args)
{
    Console.Write(add());
    //结果: 30
    Console.ReadLine();
}

覆盖默认参数

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

代码语言:javascript复制
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()有三个参数,因此前两个被覆盖了

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

代码语言:javascript复制
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()函数中

代码语言:javascript复制
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语句中,可以在执行完毕后自动释放,以免长时间占用

代码语言:javascript复制
using(FileStream fileStream = new FileStream(@"D:1.xuan",FileMode.OpenOrCreate))
{
    //读取文件
}

析构函数

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

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

代码语言:javascript复制
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");
    }
}

0 人点赞