代码演示C#各版本新功能

代码演示C#各版本新功能

C#各版本新功能其实都能在官网搜到,但很少有人整理在一起,并通过非常简短的代码将每个新特性演示出来。

  • 代码演示C#各版本新功能

    • C# 2.0版 - 2005

      • 泛型
      • 分部类型
      • 匿名方法
      • 可以为null的值类型
      • 迭代器
      • 协变和逆变
    • C# 3.0版 - 2007
      • 自动实现的属性
      • 匿名类型
      • 查询表达式(LINQ)
      • Lambda表达式
      • 表达式树
      • 扩展方法
      • var
      • 分部方法
      • 对象和集合初始值设定项
    • C# 4.0版 - 2010
      • dynamic
      • 命名参数/可选参数
      • 泛型中的协变和逆变
      • 类型等效、内置互操作类型
    • C# 5.0版 - 2012
      • async/await
      • 调用方信息
    • C# 6.0版 - 2015
      • 静态导入
      • 异常筛选器
      • 自动初始化表达式
      • Expression-bodied 函数成员
      • Null传播器
      • 字符串内插
      • nameof表达式
      • 索引初始值设定项
    • C# 7.0版本 - 2017
      • out变量
      • 元组和析构函数
      • 模式匹配
      • 本地函数
      • 更多的expression-bodied成员
      • Ref 局部变量和返回结果
      • 弃元
      • 二进制文本和数字分隔符
      • throw表达式
    • C# 8.0 版 - 2019
      • Readonly 成员
      • 默认接口方法
      • 模式匹配增强
        • 属性模式
        • Tuple模式
        • 位置模式
      • switch表达式
      • using声明
      • 静态本地函数
      • 异步流
      • 索引和范围
      • Null合并赋值
      • 非托管构造类型
      • 嵌套表达式中的 stackalloc
  • 附录/总结

C# 2.0版 - 2005

泛型

Java中的泛型不支持值类型,且会运行时类型擦除,这一点.NET更优秀。

// Declare the generic class.
public class GenericList<T>
{
    public void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();
        list1.Add(1);

        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();
        list2.Add("");

        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
        list3.Add(new ExampleClass());
    }
}

分部类型

拆分一个类、一个结构、一个接口或一个方法的定义到两个或更多的文件中是可能的。 每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组合起来。

public partial class Employee
{
    public void DoWork()
    {
    }
}

public partial class Employee
{
    public void GoToLunch()
    {
    }
}

匿名方法

Func<int, int, int> sum = delegate (int a, int b) { return a + b; };
Console.WriteLine(sum(3, 4));  // output: 7

可以为null的值类型

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable type:
int?[] arr = new int?[10];

迭代器

static void Main()
{
    foreach (int number in SomeNumbers())
    {
        Console.Write(number.ToString() + " ");
    }
    // Output: 3 5 8
    Console.ReadKey();
}

public static System.Collections.IEnumerable SomeNumbers()
{
    yield return 3;
    yield return 5;
    yield return 8;
}

协变和逆变

在 C# 中,协变和逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。 协变保留分配兼容性,逆变则与之相反。

// Assignment compatibility.
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type.
object obj = str;  

// Covariance.
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument
// is assigned to an object instantiated with a less derived type argument.
// Assignment compatibility is preserved.
IEnumerable<object> objects = strings;  

// Contravariance.
// Assume that the following method is in the class:
// static void SetObject(object o) { }
Action<object> actObject = SetObject;
// An object that is instantiated with a less derived type argument
// is assigned to an object instantiated with a more derived type argument.
// Assignment compatibility is reversed.
Action<string> actString = actObject;

C# 3.0版 - 2007

自动实现的属性

// This class is mutable. Its data can be modified from
// outside the class.
class Customer
{
    // Auto-implemented properties for trivial get and set
    public double TotalPurchases { get; set; }
    public string Name { get; set; }
    public int CustomerID { get; set; }

    // Constructor
    public Customer(double purchases, string name, int ID)
    {
        TotalPurchases = purchases;
        Name = name;
        CustomerID = ID;
    }

    // Methods
    public string GetContactInfo() { return "ContactInfo"; }
    public string GetTransactionHistory() { return "History"; }

    // .. Additional methods, events, etc.
}

class Program
{
    static void Main()
    {
        // Intialize a new object.
        Customer cust1 = new Customer(4987.63, "Northwind", 90108);

        // Modify a property.
        cust1.TotalPurchases += 499.99;
    }
}

匿名类型

var v = new { Amount = 108, Message = "Hello" };  

// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and n .
Console.WriteLine(v.Amount + v.Message);

查询表达式(LINQ)

LINQ允许你可以像写SQL一样写C#代码,像这样:

from p in persons
where p.Age > 18 && p.IsBeatiful
select new
{
    p.WeChatId,
    p.PhoneNumber
}

LINQ的意义在于让C#做出了重大调整,本章中说到的lambda表达式、扩展方法、表达式树、匿名类型、自动属性等,都是LINQ的必要组成部分。

由于用扩展方法的形式也能得到一致的结果,而且还能让代码风格更加一致,所以我平时用LINQ语法较少:

// 与上文代码相同,但改成了扩展方法风格:
persons
    .Where(x => x.Age > 18 && x.IsBeatiful)
    .Select(x => new
    {
        x.WeChatId,
        x.PhoneNumber,
    });

Lambda表达式

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

表达式树

这个是LINQ的基础之一,它的作用是将代码像数据一样,保存在内存中;然后稍后对这些“代码数据”进行重新解释/执行。

Entity Framework就是一个经典场景,它先将表达式树保存起来,然后执行时,将其翻译为SQL发给数据库执行。

注意:表达式树并不能表示所有的代码,C# 3.0之后的语法,包含???.async await、可选参数等,都无法放到表达式树中。据说官方准备更新它,但迟迟没有进展。

扩展方法

扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。

static void Main()
{
    Console.WriteLine ("Perth".IsCapitalized());
    // Equivalent to:
    Console.WriteLine (StringHelper.IsCapitalized ("Perth"));   

    // Interfaces can be extended, too:
    Console.WriteLine ("Seattle".First());   // S
}

public static class StringHelper
{
    public static bool IsCapitalized (this string s)
    {
        if (string.IsNullOrEmpty(s)) return false;
        return char.IsUpper (s[0]);
    }

    public static T First<T> (this IEnumerable<T> sequence)
    {
        foreach (T element in sequence)
            return element;

        throw new InvalidOperationException ("No elements!");
    }
}

var

var i = 10; // Implicitly typed.
int i = 10; // Explicitly typed.

分部方法

namespace PM
{
    partial class A
    {
        partial void OnSomethingHappened(string s);
    }

    // This part can be in a separate file.
    partial class A
    {
        // Comment out this method and the program
        // will still compile.
        partial void OnSomethingHappened(String s)
        {
            Console.WriteLine("Something happened: {0}", s);
        }
    }
}

对象和集合初始值设定项

public class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string Name { get; set; }

    public Cat()
    {
    }

    public Cat(string name)
    {
        this.Name = name;
    }
}

C# 4.0版 - 2010

dynamic

这个是特性使得CLR不得不进行一次修改。有了这个,C#也能像jsphppython等弱类型语言一样写代码了。

dynamic a = 3;
a = 3.14;
a = "Hello World";
a = new[] { 1, 2, 3, 4, 5 };
a = new Func<int>(() => 3);
a = new StringBuilder();
Console.WriteLine(a.GetType().Name); // StringBuilder

注意dynamic可以表示任何东西,包含数组、委托等等。滥用dynamic容易让程序变得很难维护。

命名参数/可选参数

PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);
public void ExampleMethod(int required, string optionalstr = "default string",
    int optionalint = 10)

泛型中的协变和逆变

IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;
Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());

类型等效、内置互操作类型

这个主要是为了和COM进行交互。之前需要引用一些COM类型相关的程序集,现在可以直接引用COM
具体可以参见:https://docs.microsoft.com/zh-cn/dotnet/framework/interop/type-equivalence-and-embedded-interop-types

C# 5.0版 - 2012

async/await

private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}

calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work.  The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};

async/await的本质是状态机,像IEnumerable<T>一样。以前游戏引擎Unity只支持C# 3.0,因此当时它用状态机发Http请求是用的IEnumerable<T>

async/await有两个好处,一是可以避免UI线程卡顿,二是提高系统吞吐率,最终提高性能。

调用方信息

public void DoProcessing()
{
    TraceMessage("Something happened.");
}

public void TraceMessage(string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0)
{
    System.Diagnostics.Trace.WriteLine("message: " + message);
    System.Diagnostics.Trace.WriteLine("member name: " + memberName);
    System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
    System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}

// Sample Output:
//  message: Something happened.
//  member name: DoProcessing
//  source file path: c:\Visual Studio Projects\CallerInfoCS\CallerInfoCS\Form1.cs
//  source line number: 31

注意这个是编译期生成的,因此比StackTrace更能保证性能。

C# 6.0版 - 2015

静态导入

终于可以不用写静态类名了。

using static System.Math;
using static System.Console;

WriteLine(Sin(3.14)); // 0.00159265291648683

异常筛选器

try-catch时,可以按指定的条件进行catch,其它条件不catch

public static async Task<string> MakeRequest()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
        var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
        try
        {
            var responseText = await stringTask;
            return responseText;
        }
        catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
    }
}

自动初始化表达式

public ICollection<double> Grades { get; } = new List<double>();

Expression-bodied 函数成员

public override string ToString() => $"{LastName}, {FirstName}";

Null传播器

var first = person?.FirstName;

字符串内插

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

nameof表达式

有时字符串值和某个变量名称一致,尤其是在做参数验证时。这里nameof就能在编译期,自动从变量名生成一个字符串。

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

索引初始值设定项

使集合初始化更容易的另一个功能是对 Add 方法使用扩展方法 。 添加此功能的目的是进行 Visual Basic 的奇偶校验。 如果自定义集合类的方法具有通过语义方式添加新项的名称,则此功能非常有用。

C# 7.0版本 - 2017

out变量

if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

元组和析构函数

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

析构函数应该类似C++中的析构函数,在实例回收时执行?

模式匹配

现在可以在匹配一个类型时,自动转换为这个类型的变量,如果转换失败,这个变量就赋值为默认值(null0)。

极简版:

if (input is int count)
    sum += count;

switch/case版:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}

本地函数

这个主要是方便,javascript就能这样写。

lambda的好处在于,这个可以定义在后面,而lambda必须定义在前面。

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
            yield return c;
    }
}

更多的expression-bodied成员

该功能可以让一些函数写成表达式的形式,非常的方便。

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// Expression-bodied get / set accessors.
public string Label
{
    get => label;
    set => this.label = value ?? "Default label";
}

Ref 局部变量和返回结果

此功能允许使用并返回对变量的引用的算法,这些变量在其他位置定义。 一个示例是使用大型矩阵并查找具有某些特征的单个位置。

这个功能主要是为了提高值类型的性能,让它真正发挥其作用。C++就有类似的功能。

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}
ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

弃元

通常,在进行元组解构或使用out参数调用方法时,必须定义一个其值无关紧要且你不打算使用的变量。 为处理此情况,C#增添了对弃元的支持 。 弃元是一个名为_的只写变量,可向单个变量赋予要放弃的所有值。 弃元类似于未赋值的变量;不可在代码中使用弃元(赋值语句除外)。

using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

        Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
    }

    private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
    {
        int population1 = 0, population2 = 0;
        double area = 0;

        if (name == "New York City")
        {
            area = 468.48;
            if (year1 == 1960)
            {
                population1 = 7781984;
            }
            if (year2 == 2010)
            {
                population2 = 8175133;
            }
            return (name, area, year1, population1, year2, population2);
        }

        return ("", 0, 0, 0, 0, 0);
    }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

二进制文本和数字分隔符

这个用于使数字和二进制更可读。

// 二进制文本:
public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

// 数字分隔符:
public const long BillionsAndBillions = 100_000_000_000;
public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

throw表达式

throw之前必须是一个语句,因此有时不得不写更多的代码来完成所需功能。但7.0提供了throw表达式来使代码更简洁,阅读更轻松。

void Main()
{
    // You can now throw expressions in expressions clauses.
    // This is useful in conditional expressions:

    string result = new Random().Next(2) == 0 ? "Good" : throw new Exception ("Bad");
    result.Dump();

    Foo().Dump();
}

public string Foo() => throw new NotImplementedException();

C# 8.0 版 - 2019

Readonly 成员

public readonly override string ToString() =>
    $"({X}, {Y}) is {Distance} from the origin";

默认接口方法

接口中也能定义方法了,这个新功能经常受到争论。但想想,有时是先定义接口,而实现接口需要实现很多相关、但又繁琐的功能,如ASP.NET Core中的ILogger,谁用谁知道,特别多需要实现的方法,但又都差不多。因此所以这个功能其实很有必要。

void Main()
{
    ILogger foo = new Logger();
    foo.Log (new Exception ("test"));
}

class Logger : ILogger
{
    public void Log (string message) => Console.WriteLine (message);
}

interface ILogger
{
    void Log (string message);  

    // Adding a new member to an interface need not break implementors:
    public void Log (Exception ex) => Log (ExceptionHeader + ex.Message);

    // The static modifier (and other modifiers) are now allowed:
    static string ExceptionHeader = "Exception: ";
}

模式匹配增强

这个是为简化代码、函数式编程而生的,我个人非常喜欢。

属性模式

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.75M,
        { State: "MI" } => salePrice * 0.05M,
        // other cases removed for brevity...
        _ => 0M
    };

Tuple模式

public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };

位置模式

static Quadrant GetQuadrant(Point point) => point switch
{
    (0, 0) => Quadrant.Origin,
    var (x, y) when x > 0 && y > 0 => Quadrant.One,
    var (x, y) when x < 0 && y > 0 => Quadrant.Two,
    var (x, y) when x < 0 && y < 0 => Quadrant.Three,
    var (x, y) when x > 0 && y < 0 => Quadrant.Four,
    var (_, _) => Quadrant.OnBorder,
    _ => Quadrant.Unknown
};

switch表达式

这个功能能使代码从大量的if/elseswitch/case变成“一行代码”,符合函数式编程的思想,非常好用!

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
        Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };

using声明

static int WriteLinesToFile(IEnumerable<string> lines)
{
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
    // Notice how we declare skippedLines after the using statement.
    int skippedLines = 0;
    foreach (string line in lines)
    {
        if (!line.Contains("Second"))
        {
            file.WriteLine(line);
        }
        else
        {
            skippedLines++;
        }
    }
    // Notice how skippedLines is in scope here.
    return skippedLines;
    // file is disposed here
}

静态本地函数

相比非静态本地函数,静态本地函数没有闭包,因此生成的代码更少,性能也更容易控制。

int M()
{
    int y = 5;
    int x = 7;
    return Add(x, y);

    static int Add(int left, int right) => left + right;
}

异步流

这个功能和IEnumerable<T>Task<T>对应,一个经典的表格如下:
| | 单值 | 多值 |
| ---- | ------- | -------------- |
| 同步 | T | IEnumerable |
| 异步 | Task | ?

其中,这个问号?终于有了答案,它就叫异步流——IAsyncEnumerable<T>

public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
{
    for (int i = 0; i < 20; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}

不像IEnumerable<T>IAsyncEnumerable<T>系统还没有内置扩展方法,因此可能没有IEnumerable<T>方便,但是可以通过安装NuGetf来实现和IEnumerable<T>一样(或者更爽)的效果。

索引和范围

Python中的切片器一样,只是-^代替了。

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

var quickBrownFox = words[1..4];
var lazyDog = words[^2..^0];
var allWords = words[..]; // contains "The" through "dog".
var firstPhrase = words[..4]; // contains "The" through "fox"
var lastPhrase = words[6..]; // contains "the", "lazy" and "dog"

Null合并赋值

List<int> numbers = null;
int? i = null;

numbers ??= new List<int>();
numbers.Add(i ??= 17);
numbers.Add(i ??= 20);

Console.WriteLine(string.Join(" ", numbers));  // output: 17 17
Console.WriteLine(i);  // output: 17

非托管构造类型

与任何非托管类型一样,可以创建指向此类型的变量的指针,或针对此类型的实例在堆栈上分配内存块

Span<Coords<int>> coordinates = stackalloc[]
{
    new Coords<int> { X = 0, Y = 0 },
    new Coords<int> { X = 0, Y = 3 },
    new Coords<int> { X = 4, Y = 0 }
};

嵌套表达式中的 stackalloc

Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6 ,8 });
Console.WriteLine(ind);  // output: 1

附录/总结

这么多功能,你印象最深刻的是哪个呢?

参考资料:C#发展历史 - C#指南 | Microsoft Docs https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-version-history

本文内容和代码由肖鹏整理,有修改;转载已获得肖鹏本人授权。肖鹏是我公司从Java.NET的同事。原文链接为:https://akiyax.github.io/new-features-in-csharp/

喜欢的朋友请关注我的微信公众号:【DotNet骚操作】

原文地址:https://www.cnblogs.com/sdflysha/p/20200228-csharp-history-code-demo.html

时间: 2024-10-09 02:58:58

代码演示C#各版本新功能的相关文章

Kafka 0.11版本新功能介绍 —— 空消费组延时rebalance

在0.11之前的版本中,多个consumer实例加入到一个空消费组将导致多次的rebalance,这是由于每个consumer instance启动的时间不可控,很有可能超出coordinator确定的rebalance timeout(即max.poll.interval.ms),而每次rebalance的代价又相当地大,因为很多状态都需要在rebalance前被持久化,而在rebalance后被重新初始化.曾经有个国外用户,他有100个consumer,每次rebalance的时间甚至要1个

OpenStack Q版本新功能以及各核心组件功能对比

OpenStack Q版本已经发布了一段时间了.今天, 小编来总结一下OpenStack Q版本核心组件的各项主要新功能, 再来汇总一下最近2年来OpenStack N.O.P.Q各版本核心组件的主要新功能.仅供参考, 如有遗漏.错误请指正. 1.1         Q版新功能总结 Q版相对于P版, 主要还是各功能的增强和优化, 其中主要功能有: 计算组件中的vGPU支持.冷迁移至指定主机.PCI NUMA亲和性.卷共享等,镜像组件中的web方式导入镜像, 网络组件中的浮动IP QoS.DVR/

Apache Kafka 0.11版本新功能简介

Apache Kafka近日推出0.11版本.这是一个里程碑式的大版本,特别是Kafka从这个版本开始支持"exactly-once"语义(下称EOS, exactly-once semantics).本文简要介绍一下0.11版本主要的功能变更,下面中的每一项都值得专门写篇文章好好聊聊. 一.修改unclean.leader.election.enabled默认值 Kafka社区终于下定决心要把这个参数的默认值改成false,即不再允许出现unclean leader选举的情况,在正确

XenApp和XenDesktop 7.8版本新功能

1.AppDisk技术的应用程序管理和交付 7.8版本包括了最新的AppDisk应用分层技术,让我们可以独立于主桌面或服务器的镜像之外打包和管理应用程序.其操作流程是当我们根据一个标准的模板发布桌面后,当其中有差异化需求的员工需要立即安装使用一个应用程序的时候,我们不需要在去更新我们的主模板镜像,而是使用AppDisk技术将应用打包后直接置备给该员工使用.这样的应用分发方式在随时随地都可以进行,而且不影响我们整体的主镜像模板.这样的方式也可以减少我们在主镜像上应用程序复杂性. 2.整合AppDi

Ceph Luminous版本新功能预览

今天来聊一聊Ceph新版本功能,Ceph会在今年秋季发布一个长期支持稳定版本Luminous(12.x.x),现在已经出RC版了,Luminous版本新增了很多功能,比如新增一个内置的Dashboard.底层的存储引擎的变更.消息方式的改变等等. 下面我们来一起看看新版本的变化: 1.默认的消息处理从SimpleMessenger变成了AsyncMessenger 2.默认的后端存储从filestore变成了bluestore了 3.内置Dashboard预览 目前来说Ceph的新版本Dashb

[Android P] Android P版本 新功能介绍和兼容性处理(一)

cp from :https://blog.csdn.net/yi_master/article/details/80046696 Android P版本已经到来,首篇我们当然要先看下Android P版本的搭建和模拟器的使用 1: Android studio的版本请选用 Android Studio 3.1 或者 Android Studio 3.2 Canary: Android Studio 3.2 Canary下载链接 这个环境搭建还是比较容易的,就不多叙述了 在安装Android S

hasura graphql-engine v1.0.0-alpha26 版本新功能试用

hasura graphql-engine v1.0.0-alpha26 已经发布了,有好多新的变动,测试使用docker 环境,同时pg 数据库使用了citus citus 是一个方便扩展的pg 数据库扩展解决方案,很不错. 环境准备 docker-compose && citus docker-compose 文件 version: '2.1' services: graphql-engine: image: hasura/graphql-engine:v1.0.0-alpha26 p

C#各版本新功能 C#8.0

ReadOnly 成员 默认接口方法 接口里面的方法都是虚方法,字类不用写override 就能对其进行覆盖; 这与抽象类里面的方法是不同的,接口管理的更加随意;以后都可以用面向接口开发了; 若是同时继承了接口1跟接口2,接口1,2都实现了方法 TurnOnFor public interface Interface1 { public void TurnOnFor(int duration) { Task.Delay(duration); Console.WriteLine("我是接口 Int

PHP5各个版本的新功能和新特性总结

因为 PHP 那“集百家之长”的蛋疼语法,加上社区氛围不好,很多人对新版本,新特征并无兴趣.本文将会介绍自 PHP5.2 起,直至 PHP5.6 中增加的新特征 本文目录:PHP5.2 以前:autoload, PDO 和 MySQLi, 类型约束PHP5.2:JSON 支持PHP5.3:弃用的功能,匿名函数,新增魔术方法,命名空间,后期静态绑定,Heredoc 和 Nowdoc, const, 三元运算符,PharPHP5.4:Short Open Tag, 数组简写形式,Traits, 内置