ccruiの博客

ccruiの博客

C# 语法糖集合

34
2022-05-11

定义

语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。

它可以使程序员更加容易去使用这门语言:操作可以变得更加清晰、方便,或者更加符合程序员的编程习惯。

简而言之,语法糖就是一种便捷写法。

自动属性

未简化:手写私有变量+公有属性

private string _name;
public string Name
{
    get { return _name; }
    set { _name = value; }
}

简化:声明空属性,编译器自动生成对应私有成员字段。

public string Name { get; set; }

参数默认值和命名参数

使用的指导原则:

  1. 可以为方法和有参属性指定默认值
  2. 有默认值的参数,必须定义在没有默认值的参数之后
  3. 默认参数必须是常量
  4. ref和out参数不能指定默认值
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }

    // 自动属性默认初始化
    public string Address { get; set; } = "江苏";

    // 设置参数默认值(age)
    public User(string name, string address, int age = 18)
    {
        this.Name = name;
        this.Age = age;
        this.Address = address;
    }
}

// 命名参数(指定address参数)
User user = new User("小王",address:"苏州");

类型实例化

// 使用对象初始化器:{},使用对象初始化器,必须提供一个无参的构造函数,可以只给部分属性初始化
User user = new User()
{
    Name = "小王",
    Age = 18,
    Address = "苏州",
 };

组件懒加载

private Rigidbody2D _r2d;
public Rigidbody2D r2d => _r2d ??= GetComponent<Rigidbody2D>();
​
//等价于
//public Rigidbody2D r2d{
//    get{
//        if(_r2d == null)
//            _r2d = GetComponent<Rigidbody2D>();
//        return _r2d;
//    }
//}

集合

初始List集合的值

// 简化之前
List<string> listString = new List<string>();
listString.Add("小王");
listString.Add("小贤");

// 简化后
List<string> listString = new List<string>() {
    "小王",
    "小贤",
};

取List中的值

// 简化之前
foreach (string str in listString)
{
    Console.WriteLine(str);
}
// 简化之后
listString.ForEach(s => Console.WriteLine(s));

隐式类型(var)

程序员在声明变量时可以不指定类型,由编译器根据值来指定类型
var定义变量有以下四个特点:

  1. 必须在定义时初始化
  2. 一旦初始化完成,就不能再给变量赋与初始值不同类型的值了
  3. var要求是局部变量
  4. 使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样
            // 1.隐式类型在定义时必须初始化
            //var name; 报错
            var name = "小王";
            // 2.可以用同类型的其他隐式类型变量来初始化新的隐式类型变量
            var age = 18;
            var ageTwo = age;
            // 3.也可以用同类型的字面量来初始化隐式类型变量
            var name = "小王";
            name = "小贤";
            // 4.隐式类型局部变量还可以初始化数组而不指定类型
            var array = new List<string>() { "小王","小贤",};

扩展方法

扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。
注意:定义扩展方法的类必须和使用地方的类命名空间相同(如果不同命名空间记得先引入命名空间)

// 为string 添加一个扩展方法(判断string是否为空||null)
public static bool IsEmpty(this string str)
{
    return string.IsNullOrEmpty(str);
}

匿名类型(Anonymous type)

匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。 类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。可通过使用 new 运算符和对象初始值创建匿名类型。

匿名类型的限制:

  1. 匿名类型不支持事件、自定义方法和自定义重写
  2. 匿名类型是隐式封闭的
  3. 匿名类型的实例创建只使用默认构造函数
  4. 匿名类型没有提供可供控制的类名称(使用var定义的)
var unUser = new { Name = "小王", Age = 18, Phone = "123456" };

匿名方法(Anonymous methods)

普通方法定义方式,因为方法的存在是为了复用一段代码,所以一般会给方法取个名字,这个方法的引用就可以通过“方法名”调用匿名方法:
但是有的方法,不需要复用,仅仅是使用一次就够了,所以不需要方法名,这种方法就叫做匿名方法。
匿名方法必须结合委托使用。(潜在的意思就是:尽管没有方法名了,但方法的指针还是存放了某个委托对象中)

注意:

  1. 在编译后,会为每个匿名方法创建一个私有的静态方法,然后将此静态方法传给委托对象使用
  2. 匿名方法编译后会生成委托对象,生成方法,然后把方法装入委托对象,最后赋值给声明的委托变量
  3. 匿名方法可以省略参数,编译的时候会自动为这个方法按照委托签名的参数添加参数

dynamic动态对象

.net4.0中引入了一个新类型 dynamic.该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查.大多数情况下,该对象就像具有类型 object 一样.在编译时,将假定类型化为 dynamic 的元素支持任何操作

dynamic dy = "string";
Console.WriteLine(dy.GetType());
//输出:System.String
dy = 100;
Console.WriteLine(dy.GetType());
//输出:System.Int32

字符串嵌入值

c#6.0后新增的特性 $,用于代替string.Format("")

// 之前
var str=string.Format("时间:{0}", DateTime.Now);
// 改进
var str=$"时间:{DateTime.Now}";

问号(?)表达式

可空类型修饰符(?)

引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空。

string str = null;// 正确
//int i = null; // 错误

为了使值类型也可为空,就可以使用可空类型,,可以使用 Nullable泛型结构来定义,但是微软为了语法更简洁,提供了?运算符来定义可空类型,二者效果是一样的,基本语法如下:
值类型? 变量名=值;

// 这二种定义可空类型的方法都是对的,但是?更简洁
Nullable<int> intOne = 5;
int? intTwo = 5;

三元(运算符)表达式(?: )

int gender = 1;
string str = gender == 1 ? "男" : "女";

合并运算符(??)

合并运算符使用??表示,当进行转换时,如果可空类型的值为 null,则使用默认值。

int? intA = null;
int intB = 100;
intB = intA ?? 0;//此处intA为null,则将0赋值给intB

int? intC = 500;
intB = intC ?? 0;//此处intC不为null,则将500赋值给intB

NULL检查运算符(?.)

C# 6.0中,引入了 ?. 的运算符,可帮助编写更少的代码来处理 null 检查
如果对象为NULL,则不进行后面的获取成员的运算,直接返回NULL

string str = "abcd";
string s1 = null;
Console.WriteLine(str?.Length);// 输出 4
Console.WriteLine(s1?.Length);// 输出空

"?[]"运算符

int? first = customers?[0].Orders.Count();

使用完毕后自动释放资源(Using || try finally)

为了节约资源,每次使用完毕后都要释放掉资源,其中可以使用Using和try finally来进行释放资源操作。需要注意的是使用Using释放资源的对象都必须继承IDisposable接口(MSDN)。

// try finally 写法
SqlConnection conn = null;
try
{
    conn = new SqlConnection("数据库连接字符串");
    conn.Open();
}
finally
{
    conn.Close();
    conn.Dispose();
}

// Using写法
using (SqlConnection conn=new SqlConnection("数据库连接字符串"))
{
      conn.Open();
}

委托

基本用法

在.net 1.1时我们不得不声明方法后才在委托中使用
在.net 2.0之后我们可以使用匿名委托,他不单可以简化写法,还可以在匿名委托中访问范围内的变量;
再后来Lambda表达式来了,写法就更简便了。

class MyClass
{
    //定义委托
    public delegate void TestDelegate(string str);
    //定义委托方法
    public void Method(string str)
    {
        Console.WriteLine(str);
    }
    public void UseDelegate(TestDelegate d, string str)
    {
        d(str);
    }
}

//调用委托
MyClass mc = new MyClass();
//调用定义的委托方法
mc.UseDelegate(new MyClass.TestDelegate(mc.Method), "Hello!");

//使用匿名委托
mc.UseDelegate(delegate(string str)
{
    Console.WriteLine(str);
}, "Hello!");
            
//使用Lambda表达式
mc.UseDelegate(s =>
{
    Console.WriteLine(s);
}, "Hello!");

内置委托

CLR环境中给我们内置了几个常用委托Action、 Action、Func、Predicate,一般我们要用到委托的时候,尽量不要自己再定义一 个委托了,就用系统内置的这几个已经能够满足大部分的需求,且让代码符合规范。

  • 如果要委托的方法没有参数也没有返回值就想到Action
  • 有参数但没有返回值就想到Action<T>
  • 无参数有返回值、有参数且有返回值就想到Func<T>
  • 有bool类型的返回值,多用在比较器的方法,要委托这个方法就想到用Predicate<T>

Action

Action封装的方法没有参数也没有返回值,声明原型为:

public delegate void Action();

用法如下:

public void Alert()
{
    Console.WriteLine("这是一个警告");
}

Action t = new Action(Alert); //  实例化一个Action委托
t();

如果委托的方法里的语句比较简短,也可以用Lambd表达式直接把方法定义在委托中,如下:

Action t = () => { Console.WriteLine("这是一个警告"); };
t();

Action

Action是Action的泛型实现,也是没有返回值,但可以传入最多16个参数,两个参数的声明原型为:

public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);

用法如下:

private void ShowResult(int a, int b)
{
    Console.WriteLine(a + b);
}

Action<int, int> t = new Action<int, int>(ShowResult);//两个参数但没返回值的委托
t(2, 3);

同样也可以直接用Lambd表达式直接把方法定义在委托中,代码如下:

Action<int, int> t = (a,b) => { Console.WriteLine(a + b); };
t(2, 3);

Func

Func委托始终都会有返回值,返回值的类型是参数中最后一个,可以传入一个参数,也可以最多传入16个参数,但可以传入最多16个参数,两个参数一个返回值的声明原型为:

public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);

用法如下:

public bool Compare(int a, int b)
{
    return a > b;
}

Func<int, int, bool> t = new Func<int, int, bool>(Compare);//传入两个int参数,返回bool值
bool result = t(2, 3);

同样也可以直接用Lambd表达式直接把方法定义在委托中,代码如下:

Func<int, int, bool> t = (a, b) => { return a > b; };
bool result = t(2, 3);

Predicate

Predicate委托表示定义一组条件并确定指定对象是否符合这些条件的方法,返回值始终为bool类型,声明原型为:

public delegate bool Predicate<in T>(T obj);

用法如下:

public bool Match(int val)
{
    return val > 60;
}

Predicate<int> t = new Predicate<int>(Match);   //定义一个比较委托
int[] arr = { 13, 45, 26, 98, 3, 56, 72, 24 };
int first = Array.Find(arr, t);                 //找到数组中大于60的第一个元素

同样也可以直接用Lambd表达式直接把方法定义在委托中,代码如下:

Predicate<int> t = val => { return val > 60;};   //定义一个比较委托
int[] arr = { 13, 45, 26, 98, 3, 56, 72, 24 };            
int first = Array.Find(arr, t);                  //找到数组中大于60的第一个元素

Lambda表达式

Lambda表达式是比匿名方法更简洁的一种匿名方法语法
最基本的Lambda表达式语法:
{参数列表}=>{方法体}
例如:
(int x)=>{returm x+1}
说明:
1、参数列表中的参数类型可以是明确类型或推断类型
2、如果是推断类型,则参数的数据类型将由编译器根据上下文自动推断出来

如果参数列表只包含一个推断类型参数时:
参数列表=>{方法体}
前提:x的数据类型可以根据上下文推断出来
x =>{returm x+1}

如果方法体只包含一条语句时:
{参数列表}=>表达式
{int x} => x+1;

Lambda表达式示例:
1、多参数,推断类型参数列表,表达式方法体
(x,y) => x*y
2、无参数,表达式方法体
() => Console.WriteLine()
3、多参数,推断类型参数列表,多语句方法体,需要使用{}
(x,y) => {Console.WriteLine(x);Console.WriteLine(y)}

Lambda表达式缩写推演
new Func<string,int>(delegate(string str){return str.Length;});//内置委托
delegate(string str){return str.Length;}//匿名方法
(string str)=>{return str.Length};//Lambda表达式
(str)=>str.Length;//让编译器推断类型
str=>str>Length;//去掉不必要的括弧