C#新特性记录

2021-02-10 18:16

阅读:390

C#6.0新特性笔记

Getter专属赋值

可以在构造函数中,给只有get的属性赋初始值。

class Point
    {
        public int x { get; }
        public Point()
        {
            x = 1;
        }
    }

自动属性初始化

可以给自动属性,赋初始化值

class Point
    {
        public int x { get; set; } = 1;
    }

全局静态

可以设置全局静态,然后直接写静态类的方法。

using static System.Console;

namespace FxApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Read();
        }
    }
}

全局枚举

可以设置全局枚举,然后直接写枚举属性

using static System.ConsoleColor;

namespace FxApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var color = Red;
        }
    }
}

字符串插入

可以使用$来进行字符串插入,替代string.format。全局变量或参数都可插入

internal class Program
    {
        public static int x { get; set; }
        private static void Main(string[] args)
        {
            string text = $"{x},{args}";
        }
    }

Expression bodied

可以使用箭头表达式来给方法或属性提供表达式体。注意,表达式体不能有括号。

 internal class Program
    {
        public string Name => "Chenxy";
        public int Sum(int x, int y) => x + y;
        private static void Main(string[] args)
        {
            Program po = new Program();
            Console.WriteLine(po.Name); //Chenxy
            Console.WriteLine(po.Sum(1, 2)); //3
            Console.Read();
        }
    }

索引值初始化

可以给集合赋初始化值

internal class Program
    {
        IDictionary dict = new Dictionary()
        {
            [1] = "first",
            [2] = "second"
        };
        private static void Main(string[] args)
        {
            Program po = new Program();
            foreach (var item in po.dict)
            {
                Console.WriteLine(item.Value);
            }
            Console.Read();
        }
    }

?.运算符

空值运算符,可以简化判断null的工作

public class Point
    {
        public int x { get; } = 1;
    }
    internal class Program
    {
        public static void Main(string[] args)
        {
            Point po = null;
            //C# 5.0 写法
            if (po != null)
            {
                Console.WriteLine(po.x);
            }
            //C# 6.0 写法
            Console.WriteLine(po?.x);
            Console.Read();
        }
    }

nameof表达式

为了防止重构时忘记修改,可以使用nameof来将一个常量和变量绑定。变量改变时,常量随之改变

internal class Program
    {
        public static void Main(string[] args)
        {
            string error = string.Empty;
            //C# 5.0
            Console.WriteLine("error"); //error
            //C# 6.0
            Console.WriteLine(nameof(error)); //error

            Console.Read();
        }
    }

在此代码中,如果改变 error名称,但不修改nameof(error)名称。则会在编译时提示

异常过滤器

可以在try catch中进行判断,来控制是否进入catch。如果hideError为false,则不会进入catch。

public static void Main(string[] args)
        {
            bool hideError = false;
            try
            {
                throw new Exception();
            }
            catch (Exception ex) when (hideError)
            {
                Console.WriteLine("异常已抛出");
            }

            Console.Read();
        }

catch和finally中使用await

可以在catch 和 finally中,使用await。

internal class Program
    {
        public async static void Main(string[] args)
        {
            HttpClient client = null;
            try
            {
                var result = await client.GetAsync("");
            }
            catch (Exception ex)
            {
                var result = await client.GetAsync("");
            }
            finally
            {
                var result = await client.GetAsync("");
            }

            Console.Read();
        }
    }

Visual Studio 2017

https://zhuanlan.zhihu.com/p/25626653

C#7.0新特性笔记

Out

原先代码中,如果使用Out参数,需要预先定义变量。而C#7中,则可以直接在输出参数中定义变量。

我们也可以直接在参数中,定义var隐式变量。

//VS2015
static void Main(string[] args)
        {
            int x;
            OutMethod(out x);
            WriteLine($"X:{x}");
            Read();
        }

//VS2017
static void Main(string[] args)
        {
            OutMethod(out int x);
            WriteLine($"X:{x}");
            Read();
        }

解构

在调用函数中,可以将参数组合封装到一个类的构造函数中。但是无法通过对象解构成各个组成部分。

通过解构函数,则可以完成这个事情。解构函数可以返回对象的具体确定组件。

例如,我们模拟一个参数组合

public class PathInfo {
        public string DirectoryName { get; set; }
        public string FileName { get; set; }
        public string Extension { get; set; }
        public PathInfo(string dname, string fname, string extension)
        {
            DirectoryName = dname;
            FileName = fname;
            Extension = extension;
        }
    }

我们需要从nuget加载 System.ValueTuple这样,才可以使用元组进行解构。

我们如果要使用解构,就需要先定义一个解构函数:Desconstruct

public void Deconstruct(out string dname, out string fname, out string extension)
        {
            dname = DirectoryName;
            fname = FileName;
            extension = Extension;
        }

需要注意的是,必须使用out,名称可以不一样,他是按照参数顺序返回的。

接下来,现在我们使用解构函数来调用。我们展示三种示例,来进行调用测试。

class Program
    {
        static void Main(string[] args)
        {
            PathInfo info = new PathInfo("d", "f", "e");
            
            //示例一:参数内声明。也可以使用var
            (string dname, string fname, string extension) = info;
            WriteLine($"{dname},{fname},{extension}");

            //示例二:声明值初始化。分配现有变量
            string dname1, fname1, extension1 = null;
            (dname1, fname1, extension1) = info;
            WriteLine($"{dname1},{fname1},{extension1}");

            //示例三:隐式变量声明
            var (dname2, fname2, extension2) = info;
            WriteLine($"{dname2},{fname2},{extension2}");
            Read();
        }
    }

跟元组类似的写法,会给每个值进行赋值,只要顺序对应即可。

注意:在示例三种,我们在括号外面声明的var,这样括号内的三个变量类型必须一致。

解构函数,要求圆括号内至少有两个输出变量。如果只有一个是不起作用的,例如

(string dname) = info; //这样是不行的。Deconstruct,如果只有一个方法是不行的。

Deconstruct,可以进行重载。使用的时候,会自动找对应的重载方法。

元组

通过元组可以从一个方法中返回多个值。在C#7.0之前,我们会定义输出参数或者对象集合。

我们可以通过两种方式,来定义元祖。

1.不使用参数名,只填写参数类型。这样会返回 Item1,2,3

2.使用参数名,并填写参数类型。这样会返回Item.参数名

3.直接使用参数名。和2一个效果,但是方便一些。

class Program
    {
        static void Main(string[] args)
        {
            PathInfo info = new PathInfo();

            var item = info.getEmpInfo();
            WriteLine($"{item.Item1},{item.Item2},{item.Item3}");

            var item2 = info.getEmpInfo2();
            WriteLine($"{item2.a},{item2.b},{item2.c}");

            var item3 = info.getEmpInfo3();
            WriteLine($"{item3.a},{item3.b},{item3.c}");

            Read();
        }
    }

    public class PathInfo
    {
        // 不使用参数名,返回Item1,2,3
        public (string, string, string) getEmpInfo()
        {
            return ("a", "b", "c");
        }
        // 使用参数名,返回定义参数名
        public (string a, string b, string c) getEmpInfo2()
        {
            return ("a", "b", "c");
        }
        // 使用指定元素
        public (string a, string b, string c) getEmpInfo3()
        {
            return (a: "a", b: "b", c: "c");
        }
    }

模式匹配 

有时候基类会派生一些子类。我们如果要对每个类实施Eject方法。可以使用多个选择来实现。

AS运算符

使用AS运算符转换并赋值

检查结果是否为null

执行Eject操作

IS运算符

使用IS运算符检查类型

转换类型并为其赋值

执行Eject操作

Cast

显示转换赋值

捕捉可能的异常

执行操作

上述这些方法存在的问题都是语法相当冗长,总要为转换的类提供多个语句

具有IS表达式的模式匹配

C#7.0提供模式匹配,用作一种将测试和赋值合并为单个操作方法。

举个例子,我们在C#7以前,进行类型转换如下。我们使用IS运算符

class Program
    {
        static void Main(string[] args)
        {
            Eject(new Woman());
            Read();
        }

        static void Eject(People peo)
        {
            if (peo is Woman)
            {
                Woman wo = (Woman)peo;
                wo.Con();
            }
        }
    }

    class People {}

    class Woman : People {
        public void Con() {
            WriteLine("This is Woman");
        }
    }

可以看出来,我们需要进行 is,然后进行转换。现在我们使用模式匹配来实现上面代码

static void Eject(People peo)
        {
            if (peo is Woman wo)
            {
                wo.Con();
            }
        }

模式匹配将测试和赋值合并成一个操作了。 

Switch语句的模式匹配

使用Switch可以在多个可转换的兼容类型之间时更加重要。

如果我们想添加一个附加条件,可以使用when附加在case子句。

static void Eject(People peo)
        {
            switch (peo)
            {
                case Woman wo when wo != null:
                    wo.Con();
                    break;
                case People pe:
                    break;
                default:
                    break;
            }
        }

本地函数

C#7.0允许在一个成员内部完全声明本地函数。来替代Action和Func.

本地函数的作用于域,进在周围函数的内部。关于本地函数有以下几个注意点

1.不允许修饰符

2.不支持重载

3.本地函数可以访问所有变量,包括局部变量

4.本地函数存在整个方法范围内,声明不分前后

bool IsPalindrome(string text)
{
  if (string.IsNullOrWhiteSpace(text)) return false;
  bool LocalIsPalindrome(string target)
  {
    target = target.Trim();  // Start by removing any surrounding whitespace.
    if (target.Length ;
    else
    {
      return char.ToLower(target[0]) ==
        char.ToLower(target[target.Length - 1]) &&
        LocalIsPalindrome(
          target.Substring(1, target.Length - 2));
    }
  }
  return LocalIsPalindrome(text);
}

通过引用返回

通过ref可以将参数传递给函数来进行更新。

C#7.0除了ref参数外,还可以通过函数返回一个引用。

class Program
    {
        static void Main(string[] args)
        {
            int[] result = { 1, 2, 3 };
            ref int a = ref Find(result);
            a += 1;
            WriteLine(result[0]);
            Read();
        }

        static ref int Find(int[] sum)
        {
            return ref sum[0];
        }
    }

文本改进

C#7.0可以通过下划线来提高可读性。可以放在二进制、十进制、十六进制数字的任意位置

long LargestSquareNumberUsingAllDigits =
  0b0010_0100_1000_1111_0110_1101_1100_0010_0100;  // 9,814,072,356
long MaxInt64 { get; } =
  9_223_372_036_854_775_807;  // Equivalent to long.MaxValue

Throw表达式

可以在表达式中,直接抛出异常

public string getEmpInfo( string EmpName)
    {
        string[] empArr = EmpName.Split(",");
        return (empArr.Length > 0) ? empArr[0] : throw new Exception("Emp Info Not exist");
    }

异步返回类型

编译器不限制异步方法必须返回 void、Task、Task

任何可以继承 GetAwaiter   方法的都可以进行返回。

但是,此方法我验证不通过。故此暂时不考虑这个特性。

Expression bodied 增强

C#7.0里面把箭头函数表达式,进行了增强。

我们可以直接在构造函数、析构函数中使用

public class Point
    {
        public Point() => WriteLine("构造");

        ~Point() => WriteLine("释放");
    }

 

$


评论


亲,登录后才可以留言!