首页 > 编程语言 >C#7.0之后新特性

C#7.0之后新特性

时间:2024-03-20 10:35:08浏览次数:7  
标签:C# 特性 元组 int 7.0 WriteLine ref public

目录

C#7

C#7.0新特性

C# 7.3在2017年3月发布

out 变量(out variables)

以前我们使用out变量必须在使用前进行声明,C# 7.0 给我们提供了一种更简洁的语法 “使用时进行内联声明” 。如下所示:

var input = ReadLine();
if(int.TryParse(input, out var result))
{
    WriteLine("您输入的数字是:{0}", result);
}
else
{
    WriteLine("无法解析输入...");
}

等价于以前的

int num;
string s = Console.ReadLine();
if(int.TryParse(s, out num))
{
    Console.WriteLine("您输入的数字是:{0}", num);
}
else
{
    Console.WriteLine("无法解析输入...");
}

原理解析:所谓的 “内联声明” 编译后就是以前的原始写法,只是现在由编译器来完成。
 备注:在进行内联声明时,即可直接写明变量的类型也可以写隐式类型,因为out关键字修饰的一定是局部变量。

元组(Tuples)

元组(Tuple)在 .Net 4.0 的时候就有了,但元组也有些缺点,如:

1)Tuple 会影响代码的可读性,因为它的属性名都是:Item1,Item2.. 。

2)Tuple 还不够轻量级,因为它是引用类型(Class)。

备注:上述所指 Tuple 还不够轻量级,是从某种意义上来说的或者是一种假设,即假设分配操作非常的多。

C# 7 中的元组(ValueTuple)解决了上述两个缺点:

1)ValueTuple 支持语义上的字段命名。

2)ValueTuple 是值类型(Struct)。

如何创建一个元组

var tuple = (1, 2);                           // 使用语法糖创建元组
var tuple2 = ValueTuple.Create(1, 2);         // 使用静态方法【Create】创建元组
var tuple3 = new ValueTuple&<int, int>(1, 2);  // 使用 new 运算符创建元组  
WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三种方式都是等价的。");

原理解析:上面三种方式最终都是使用 new 运算符来创建实例。

如何创建给字段命名的元组

// 左边指定字段名称
(int one, int two) tuple = (1, 2);
WriteLine($ "first:{tuple.one}, second:{tuple.two}");
// 右边指定字段名称
var tuple2 = (one: 1, two: 2);
WriteLine($ "first:{tuple2.one}, second:{tuple2.two}");
// 左右两边同时指定字段名称
(int one, int two) tuple3 = (first: 1, second: 2); /* 此处会有警告:由于目标类型(xx)已指定了其它名称,因为忽略元组名称xxx */
WriteLine($ "first:{tuple3.one}, second:{tuple3.two}");

注:左右两边同时指定字段名称,会使用左边的字段名称覆盖右边的字段名称(一一对应)。

原理解析:上述给字段命名的元组在编译后其字段名称还是:Item1, Item2...,即:“命名”只是语义上的命名。

元组的使用

var (one, two) = GetTuple();
WriteLine($"first:{one}, second:{two}");
static (int, int) GetTuple()
{
  return (1, 2);
}

is 表达式(is expressions)

static int GetSum(IEnumerable<object> values)
{
    var sum = 0;
    if(values == null) return sum;
    foreach(var item in values)
    {
        if(item is short) // C# 7 之前的 is expressions
        {
            sum += (short) item;
        }
        else if(item is int val) // C# 7 的 is expressions
        {
            sum += val;
        }
        else if(item is string str && int.TryParse(str, out var result)) // is expressions 和 out variables 结合使用
        {
            sum += result;
        }
        else if(item is IEnumerable<object> subList)
        {
            sum += GetSum(subList);
        }
    }
    return sum;
}

原理解析:此 is 非彼 is ,这个扩展的 is 其实是 as 和 if 的组合。即它先进行 as 转换再进行 if 判断,判断其结果是否为 null,不等于 null 则执行

语句块逻辑,反之不行。由上可知其实C# 7之前我们也可实现类似的功能,只是写法上比较繁琐。

switch语句更新(switch statement updates)

基于类型的模式匹配

Circle Rectangle是自己创建的类

public void ProcessShape(object shape)
{
    switch (shape)
    {
        case Circle c:
            Console.WriteLine($"Processing Circle with radius {c.Radius}");
            break;
        case Rectangle r:
            Console.WriteLine($"Processing Rectangle with width {r.Width} and height {r.Height}");
            break;
        default:
            Console.WriteLine("Unknown shape");
            break;
    }
}

基于条件的模式匹配

public void ProcessNumber(int number)
{
    switch (number)
    {
        case int i when (i < 0):
            Console.WriteLine("Negative number");
            break;
        case int i when (i > 0):
            Console.WriteLine("Positive number");
            break;
        default:
            Console.WriteLine("Zero");
            break;
    }
}

局部引用和引用返回 (Ref locals and returns)

我们知道 C# 的 ref 和 out 关键字是对值传递的一个补充,是为了防止值类型大对象在Copy过程中损失更多的性能。现在在C# 7中 ref 关键字得

到了加强,它不仅可以获取值类型的引用而且还可以获取某个变量(引用类型)的局部引用。如:

static ref int GetLocalRef(int[, ] arr,Func<int, bool>func)
{
    for(int i = 0; i < arr.GetLength(0); i++)
    {
        for(int j = 0; j < arr.GetLength(1); j++)
        {
            if(func(arr[i, j]))
            {
                return ref arr[i, j];
            }
        }
    }
    throw new InvalidOperationException("Not found");
}

Call:

int[,] arr = { { 10, 15 }, { 20, 25 } };
ref var num = ref GetLocalRef(arr, c => c == 20);
num = 600;
Console.WriteLine(arr[1, 0]);

Print results:

使用方法:

1. 方法的返回值必须是引用返回:

a)  声明方法签名时必须在返回类型前加上 ref 修饰。

b)  在每个 return 关键字后也要加上 ref 修饰,以表明是返回引用。

2. 分配引用(即赋值),必须在声明局部变量前加上 ref 修饰,以及在方法返回引用前加上 ref 修饰。

注:C# 开发的是托管代码,所以一般不希望程序员去操作指针。并由上述可知在使用过程中需要大量的使用 ref 来标明这是引用变量(编译后其

实没那么多),当然这也是为了提高代码的可读性。

局部函数(Local functions)

C# 7 中的一个功能“局部函数”,如下所示:

public void GlobalToLocalExample()
{
    string[] words = {"the", "quick", "brown", "fox", "jumps"};

    //局部函数定义
    string ConvertArrayToString()
    {
        string result = string.Empty;

        for (var i = 0; i < words.Length; i++)
        {
            result += words[i];

            if (i != words.Length - 1)
            {
                result += " ";
            }
        }

        return result;
    }

    //调用局部函数
    string sentence = ConvertArrayToString();
    Console.WriteLine(sentence);  //输出 "the quick brown fox jumps"
}

更多的表达式体成员(More expression-bodied members)

C# 6 的时候就支持表达式体成员,但当时只支持“函数成员”和“只读属性”,这一特性在C# 7中得到了扩展,它能支持更多的成员:构造函数、析构函数、带 get,set 访问器的属性、以及索引器。如下所示:

public class ExampleClass
{
    private string _name;
    
    // 构造函数
    public ExampleClass(string name) => _name = name ?? throw new ArgumentNullException(nameof(name));

    // 属性访问器
    public string Name
    {
        get => _name;
        set => _name = value ?? throw new ArgumentNullException(nameof(value));
    }

    // 析构函数
    ~ExampleClass() => Console.WriteLine("Destructed");
}

备注:索引器其实在C# 6中就得到了支持,但其它三种在C# 6中未得到支持。

Throw 表达式(Throw expressions)

Lambda表达式中的throw

Func<int, int> function = x => x >= 0 ? x : throw new ArgumentException("x cannot be negative");

属性设置器中的throw

public class Student
{
      private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName));

      private int _age;

      public int Age
      {
          get => _age;
          set => _age = value <= 0 || value >= 130 ? throw new ArgumentException("参数不合法") : value;
      }

      static string GetName() => null;
}

扩展异步返回类型(Generalized async return types)

以前异步的返回类型必须是:Task、Task、void,现在 C# 7 中新增了一种类型:ValueTask,如下所示:

public async ValueTask<int>Func()
{
    await Task.Delay(3000);
    return 100;
}

数字文本语法的改进

C# 7 还包含两个新特性:二进制文字、数字分隔符,如下所示:

int binaryRepresentation = 0b1101;
long billGatesNetWorth = 122_000_000_000;
double avogadroConstant = 6.022_140_76e23;
int flagBits = 0b1010_1011_1100_1101_1110_1111;

注:二进制文本是以0b(零b)开头,字母不区分大小写;数字分隔符只有三个地方不能写:开头,结尾,小数点前后。

C#7.1新特性

C# 7.3在2017年8月发布

默认文本表达式

无需指定类型即可使用 default 关键字。编译器可以通过上下文自动推断其类型,例如:

int i = default;  // Equivalent to "int i = default(int);"

异步main方法

Main 方法可以被定义为异步(async)。这允许在 Main 方法中直接调用异步方法,如下例所示:

static async Task Main(string[] args)
{
    await ProcessDataAsync();
}

推断元组元素名称

var count = 5;
var label = "Colors used in the map";
var pair = (count, label); // element names are "count" and "label"

泛型类型参数的模式匹配

public static void Print<T>(T obj)
{
    if (obj is string str)
    {
        Console.WriteLine($"'{obj}' is a string: {str}");
    }
    else
    {
        Console.WriteLine($"{obj} is not a string.");
    }
}

引用程序集生成

C#7.2新特性

C# 7.3在2017年11月发布

引用语义上的只读结构体

C# 7.2 允许开发者声明只读结构体,这些结构体在作为只读变量,只读字段或只读索引器返回值时不会被复制。

例如:

public readonly struct ReadOnlyPoint
{
    public ReadOnlyPoint(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; }
    public double Y { get; }
}

private protected 访问修饰符

private protected 访问修饰符允许一个成员被其定义类型的子类型所访问,前提是这些子类型在同一组件中声明。

例如:

public class BaseClass
{
    private protected int myValue = 0;
}

public class DerivedClass1 : BaseClass
{
    void Access()
    {
        var baseObject = new BaseClass();

        baseObject.myValue = 5; // 错误
        myValue = 5; // 正确
    }
}

非尾部命名参数

C# 7.2 允许开发者在调用方法时在命名参数之后传递位置参数。

例如:

public void ExampleMethod(int required, string optionalstr = "default", int optionalint = 10)
{
    //...
}

ExampleMethod(3, optionalint:4, "non-trailing"); //正确

in 参数修饰符

C# 7.2 引入了 in 参数修饰符,以便通过引用传递参数,而不是通过值传递,这样可以减少大型结构体的复制操作来提高性能。in 修饰符不仅表明一个参数作为引用传递,而且该参数在方法中是只读的,不能被修改。

下面是使用 in 参数修饰符的一个示例:

public struct Point
{
    public double X { get; }
    public double Y { get; }

    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double Distance(in Point other)
    {
        double xDifference = X - other.X;
        double yDifference = Y - other.Y;
        return Math.Sqrt(xDifference * xDifference + yDifference * yDifference);
    }
}

public class Test
{
    public void TestMethod()
    {
        Point a = new Point(0, 0);
        Point b = new Point(3, 4);
        Console.WriteLine(a.Distance(in b)); // 输出 5
    }
}

在这个示例中,Distance 方法接受一个 in 修饰的 Point 参数 other,这样当我们传递 bDistance 方法时,b 不会被复制,而是作为引用传递。这将避免对大型结构的复制,从而提高了性能。

需要注意的是,in 参数在方法内部是只读的,你不能在方法体内修改它。例如,如果你尝试运行 other.X = 10;,编译器会报错。

ref readonly 结构

ref readonly 是一个新引入的特性,这个特性允许你返回方法的只读引用。这对于那些不希望被修改的大型结构或数组非常有用,因为你可以返回对它们的引用,而不是复制它们。这可以提高应用的性能。

此外,ref readonly 还可以与 in 修饰符一起使用,以便按引用传递只读参数。

下面是一个使用 ref readonly 的示例:

public struct LargeStruct
{
    public int One { get; }
    public int Two { get; }
    public int Three { get; }
    // 依此类推...
}

public class Container
{
    private LargeStruct largeStruct;

    public ref readonly LargeStruct GetStruct()
    {
        return ref largeStruct;
    }
}

public class Test
{
    public void TestMethod()
    {
        Container container = new Container();
        ref readonly LargeStruct largeStructRef = ref container.GetStruct();

        // largeStructRef.One = 10; // 这里会报错,因为largeStructRef 是只读引用
        int one = largeStructRef.One; // 这是可以的
    }
}

C#7.3新特性

C# 7.3在2018年5月发布

ref局部变量

现在可以使用ref关键字声明局部变量。

示例如下:

public void MyMethod()
{
    int number = 5;
    ref int refToLocal = ref number;
    refToLocal = 10; 
    Console.WriteLine(number); // 输出 "10"
}

委托约束

现在可以在泛型代码中指定委托和System.Enum作为约束。

示例如下:

public class MyGenericClass<T> where T : Delegate
{
    public void SomeMethod(T delegateInstance)
    {
        delegateInstance.DynamicInvoke();
    }
}

元组等式的比较和相等性

元组类型可以适用于==和!=比较运算符。

示例如下:

var tuple1 = (5, 10);
var tuple2 = (5, 10);
Console.WriteLine(tuple1 == tuple2); // 输出 "True"

扩展初始值设定项中的表达式变量

表达式变量现在可以在更多的上下文中使用。

示例如下:

public class B
{
   public B(int i, out int j)
   {
      j = i;
   }
}

public class D : B
{
   public D(int i) : base(i, out var j)
   {
      Console.WriteLine($"The value of 'j' is {j}");
   }
}

引用传参的重载解析

这是解决函数重载解析中的一些模糊性的改进。

示例如下:

void Method(object obj) 
{
    Console.WriteLine("object");
} 

void Method(int i) 
{
    Console.WriteLine("int");
}

void Caller() 
{
    object testObj = 1;
    Method(testObj);    // 输出 "object",而不是 "int"
    Method((int)testObj); // 输出 "int"
}

unmanaged约束

public unsafe int SizeOf<T>() where T : unmanaged
{
    return sizeof(T);
}

标签:C#,特性,元组,int,7.0,WriteLine,ref,public
From: https://www.cnblogs.com/shilinfeng/p/18084649

相关文章

  • C++ 泛型编程
    1.函数模板假设我们设计一个交换两个整型变量的值的函数,代码如下://交换两个整型变量的值的Swap函数:voidSwap(int&x,int&y){inttmp=x;x=y;y=tmp;}如果是浮点类型的变量的值交换,则替换int类型为double即可,代码如下://交换两个double型变量......
  • 伪创新之所以“伪”-UMLChina建模知识竞赛第5赛季第7轮
    DDD领域驱动设计批评文集做强化自测题获得“软件方法建模师”称号《软件方法》各章合集参考潘加宇在《软件方法》和UMLChina公众号文章中发表的内容作答。在本文下留言回答。只要最先答对前3题,即可获得本轮优胜。如果有第4题,第4题为附加题,对错不影响优胜者的判定,影响的是......
  • 【QT+QGIS跨平台编译】之八十三:【QGIS_Gui跨平台编译】—【错误处理:未定义类型QgsColo
    文章目录一、未定义类型QgsColorRamp二、错误处理一、未定义类型QgsColorRamp错误信息:二、错误处理第29行增加:#include"qgscolorramp.h"......
  • SocketIO高性能事件驱动设计探索
    来源:https://segmentfault.com/a/1190000021554678?utm_source=tag-newest背景SocketIO原生基于NodeJS实现的Web长连接技术方案,H5原生场景下通常使用websocket作为基础协议进行网络通信(客户端支持多语言),SocketIO对于长连接场景下的业务形态进行了很多方面的抽象和实现,比如:命名空......
  • Elasticsearch-批量操作(bulk)
    bulk的基础概念bulk是es提供的一种批量增删改的操作API。bulk的语法bulk对JSON串的有着严格的要求。每个JSON串不能换行,只能放在同一行,同时,相邻的JSON串之间必须要有换行(Linux下是\n;Window下是\r\n)。bulk的每个操作必须要一对JSON串(delete语法除外)。PUT/_bulk{"action":......
  • C++ Qt开发:QUdpSocket实现组播通信
    Qt是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QUdpSocket组件实现基于UDP的组播通信。组播是一种一对多的通信方式,允许一个发送者将数......
  • C# 中使对象序列化/反序列化 Json 支持使用派生类型以及泛型的方式
    C#中使对象序列化/反序列化Json支持使用派生类型以及泛型方式废话#前言#为啥想写这个博客最近自己写的框架有用到这个类似工作流,支持节点编码自定义,动态运行自定义.尽量减少动态解析这就需要确定类型.有什么好的奇思妙想可以一起来讨论噢(现在还是毛坯,测......
  • NVIDIA公司官宣最新最高性能的GPU芯片及平台 —— Blackwell GPU
    官宣视频:https://www.youtube.com/watch?v=bMIRhOXAjYk相关:https://baijiahao.baidu.com/s?id=1793921686210377001https://www.thepaper.cn/newsDetail_forward_26730622黄仁勋表示,Blackwell将成为世界上最强大的芯片。Blackwell架构的GPU拥有2080亿个晶体管,采用......
  • package-lock.json
    生成package-lock.json文件:1、运行npminstall命令,npm将自动生成package-lock.json文件。2、如果你已经安装了依赖,但是没有生成package-lock.json文件:运行npminstall--save命令,这将会更新package-lock.json文件,并确保所有依赖项都被正确记录。3、如果你想要使用package-lock......
  • 350_{"code":401,"msg":"认证失败,无法访问系统资源","data":null}
    若依框架部署Linux访问报错,401认证失败,无法访问系统资源_认证失败,无法访问系统资源_冰糖码奇朵的博客-CSDN博客报错信息链接访问nginx配置解决......