[C #] 6.0 new features,

Source: Internet
Author: User

[C #] 6.0 new features,

C #6.0 has been released for a long time. Although new features and syntaxes tend to be stable, for most programmers, to use C #6.0 in your work, you have to wait for a short period of time.
So it may not be too late to talk about the new features of the new version?

I. nameof keywords

This is definitely what I expect most from the new version. It brings great convenience to code refactoring.
Let's take a look at how it is used:

string s;Console.WriteLine(nameof(s));s = nameof(s.Length);Console.WriteLine(nameof(String));Console.WriteLine(nameof(string.Length));Console.WriteLine(nameof(string.Substring));

Running result:

s
String
LengthSubString

The preceding example shows the following points:

1. It does not care whether the variable has been initialized
2. It forms a string (compile-time) expression.
3. It can be used to obtain the type name, But nameof (string) cannot be compiled. The lower-case string is a keyword rather than a type (this is worth mentioning ...)
4. The instance attributes can be obtained directly from the brackets of the Instance type.
5. It can obtain the method name.

Then read the IL of this Code:

IL_0000: ldstr "s"IL_0005: call void [mscorlib]System.Console::WriteLine(string)IL_000a: ldstr "String"IL_000f: call void [mscorlib]System.Console::WriteLine(string)IL_0014: ldstr "Length"IL_0019: call void [mscorlib]System.Console::WriteLine(string)IL_001e: ldstr "Substring"IL_0023: call void [mscorlib]System.Console::WriteLine(string)IL_0028: ret

After compilation, there is no trace of nameof. The Compiler hardcoded the computing result of nameof, so it is a "compile-time operator ".
Applicable scenarios:

1. null reference exception information structure
2. ToString Method
3. column names bound to IList data

Let's talk about the third article. This is a very disturbing thing I 've encountered in my recent work. When I use 6.0, It will completely solve this problem...
Imagine binding a List of user-defined types to ListBox. To set DisplayMember and ValueMember, it can only be hard-coded, like this:

listBox1.DisplayMember = "ID";listBox1.ValueMember = "Content";

Once you want to change the property name of this binding type, the workload is simply unimaginable... A better way is to replace hard encoding with a set of constants, but the trouble is that you have to remember the constant name.
However, it will be nice to use nameof later. A Ctrl + R and R can all be done ~

2. [.?] Null reference judgment Operator

This is a syntactic sugar used for concise code. I personally think it is of average practical value.
Let's take a look at how to use it:

string s = null;s = s?.Substring(1);// string expr_07 = this.s;// this.s = ((expr_07 != null) ? expr_07.Substring(0) : null);Console.WriteLine(s == null);

The code in the second line is equivalent to the comments in the third line after compilation.
In addition, once [.?] is used, The returned value may be null. Therefore, only Nullable can be assigned to members of the original return value type. <?> For example:

string s = null;int? i = s?.IndexOf(".");int j = s.IndexOf(".");

Variable I will be used later. In many cases, it is still necessary to judge whether the variable is null...
At the same time, this syntactic sugar also brings ambiguity, such:

object tag = form?.Tag;

Because both Form and Tag are reference types, they can be null. If the variable tag is null, you cannot know whether the form or Tag returns null (unless you judge it again ...).

3. String embedding Value

It is also a syntactic sugar for concise code. Let's take a look at how to use it:

int i = 1;Console.WriteLine($"{nameof(i)} + 1 = {i + 1}");Console.WriteLine($"{i + 1} * {i + 1} = 4");

Running result:

i + 1 = 2
2 * 2 = 4

Then IL:

IL_0000: ldc.i4.1IL_0001: stloc.0IL_0002: ldstr "{0} + 1 = {1}"IL_0007: ldstr "i"IL_000c: ldloc.0IL_000d: ldc.i4.1IL_000e: addIL_000f: box [mscorlib]System.Int32IL_0014: call string [mscorlib]System.String::Format(string, object, object)IL_0019: call void [mscorlib]System.Console::WriteLine(string)IL_001e: ldstr "{0} * {1} = 4"IL_0023: ldloc.0IL_0024: ldc.i4.1IL_0025: addIL_0026: box [mscorlib]System.Int32IL_002b: ldloc.0IL_002c: ldc.i4.1IL_002d: addIL_002e: box [mscorlib]System.Int32IL_0033: call string [mscorlib]System.String::Format(string, object, object)IL_0038: call void [mscorlib]System.Console::WriteLine(string)IL_003d: ret

You can see the following points:

1. Braces can be used for wrap expressions
2. The same expression needs to be calculated twice.

Between the second one, it is better to use an intermediate variable to put the $ String for the operation that consumes a lot of resources, or directly use String. Format.
Note that $ and @ must be written before @ at the same time, and the content in braces in the regular expression will be calculated as a C # expression first, for example:

Regex.IsMatch("AAA", $@"A{3}");Regex.IsMatch("AAA", String.Format("A{0}", 3))

The compilation results of the first and second rows are the same. However, this compilation result is obviously not what we want. Therefore, we recommend that you do not use string embedding values on regular expressions.

Iv. lambda Method body

It is still a feature for concise code, as follows:

private void LambdaMethod() => Console.WriteLine(nameof(LambdaMethod));private string LambdaProperty => nameof(LambdaProperty);

Any method that can be done in one sentence can then discard braces and return keywords. Note that the content in the second row can only implement the get method of the attribute, so this constitutes a read-only attribute.
The above two lines are actually equivalent to the following:

private void LambdaMethod(){    Console.WriteLine("LambdaMethod");}private string LambdaProperty{    get    {        return "LambdaProperty";    }}

In earlier versions, I may also write the following:

private Action LambdaMethod = () => Console.WriteLine(nameof(LambdaMethod));

This writing method is good to say that the attribute cannot be written in this way... Of course, this writing method is generally not advisable.

V. Attribute Initiator

This feature is finally expected by the stars and the moon. Although it may not be so important, it is hard for me to understand the previous version of C...
The usage is like adding a get set before the field and adding a value after the attribute:

private string InitedProperty { get; set; } = "InitedProperty";

It looks a bit like the lambda attribute in the previous feature, but it is actually very different:

1. Attributes With attribute initiators are the same as those of the automatic set get tool. fields are automatically generated, while those with lambda properties do not automatically generate private fields.
2. The equal sign of the property initiator can only be a static member, and any expression can be used in the instance lambda property.
3. The expression after the equal sign of the property initiator is calculated only once when the type is loaded. The expression of the lambda property is calculated instantly every time the property is called.
4. the attribute initiator does not affect the attribute writability. The lambda attribute can only be read.

Based on the third article above, if the initialization expression consumes a lot of resources, you should use the attribute initiator instead of the lambda attribute.

6. Index initializer

It can be said that this syntactic sugar is an upgraded version of the Set initialization tool, making the index-based set initialization more reasonable.

You can write a Dictionary as follows:

new Dictionary<int, string>{    [1] = "a",    [5] = "e"};

The key-value relationship is clear at a glance, and it is written as follows to initialize a Dictionary:

new Dictionary<int, string>{    {1, "a"},    {5, "b"}};

Just a bunch of braces is really annoying... It should be noted that the Set initializing tool and the index initializing tool cannot be used together. Of course, I believe no one will do this either...
In addition, the following code can also be compiled, but an error occurs during running:

new List<string>{    [0] = "a"};

For Dictionary, the compiler knows that the Add method is called. For List, the compiler only knows how to assign values to the indexer...
Of course, List Index Initialization is not supported because the syntax of the Set initiator can cope with this situation, and it may also be because of the following situation:

new List<string>{    [0] = "a",    [2] = "c"};

Obviously, the Add method of List cannot complete this task...

VII. Exception Filter

This is an important part of the new features and a major change. Let's take a look at how to use it:

try{    throw new IOException("Not Throw");}catch (IOException ex) when (ex.Message != "Need Throw"){    Console.WriteLine(ex.Message);}catch (NullReferenceException ex){    Console.WriteLine(ex.Message);    throw;}

Running result:

Not Throw

If this type of filtering is put in the past, it will be very ugly:

try{    throw new IOException("Not Throw");}catch (IOException ex){    if (ex.Message != "Need Throw")    {        Console.WriteLine(ex.Message);    }    else if (ex is NullReferenceException)    {        Console.WriteLine(ex2.Message);        throw;    }
else
{
throw
}}

The key is that exceptions previously caught in the catch block cannot be passed to the next catch Block.
Let's take a look at the IL of the new code:

.try{    IL_0000: ldstr "Not Throw"    IL_0005: newobj instance void [mscorlib]System.IO.IOException::.ctor(string)    IL_000a: throw} // end .tryfilter{    IL_000b: isinst [mscorlib]System.IO.IOException    IL_0010: dup    IL_0011: brtrue.s IL_0017    IL_0013: pop    IL_0014: ldc.i4.0    IL_0015: br.s IL_002b    IL_0017: stloc.0    IL_0018: ldloc.0    IL_0019: callvirt instance string [mscorlib]System.Exception::get_Message()    IL_001e: ldstr "Need Throw"    IL_0023: call bool [mscorlib]System.String::op_Inequality(string, string)    IL_0028: ldc.i4.0    IL_0029: cgt.un    IL_002b: endfilter} // end filtercatch{    IL_002d: pop    IL_002e: ldloc.0    IL_002f: callvirt instance string [mscorlib]System.Exception::get_Message()    IL_0034: call void [mscorlib]System.Console::WriteLine(string)    IL_0039: leave.s IL_0047} // end handlercatch [mscorlib]System.NullReferenceException{    IL_003b: callvirt instance string [mscorlib]System.Exception::get_Message()    IL_0040: call void [mscorlib]System.Console::WriteLine(string)    IL_0045: rethrow} // end handler

It seems that a filter block exists. It seems that the exception in the try block construction of the first code is not in the catch block at all, which is totally different from the previous processing.
At the same time, it is noted that there is a catch block not marked with the exception type under the filter block. From the content, it is the first braces after the when corresponding to the C # code.
In the filter block, the process is roughly as follows:

1. Check the exception type. If it is set to true, go to the next step. If it is set to false, enter the catch Block that references the exception.
2. Calculate the expression in when
3. The endfilter determines the result of the previous step. If the value is true, the corresponding catch block is entered. If the value is false, the exception catch Block is referenced.

We can see that the role of when is to insert a filter block before the catch Block, the endfilter command selects whether to enter the catch Block or transfer the control to the exception handler based on the value at the top of the stack.

8. static member reference

This feature appeared in Java a long time ago, and C #6.0 finally introduced it.
In fact, when the extension method was introduced, the definition type can be understood. However, the extension method brings too many benefits.
The usage is as follows:

using static System.String;...Console.WriteLine(Concat("a", "b"));

Note that the Concat method is of the String type, that is, the static reference is for members rather than types. The using static method is not necessarily of the static type.
The benefit of this special effect is, of course, convenience and convenience. The disadvantage is also obvious, that is, the intellectual damage to the definition type is not comparable to that of the extension method, therefore, you must be cautious when using this feature.
The applicable members must be clear to everyone, such as WriteLine and Format. At first glance, we can see that the method is defined in the Console and String types, rather than the current type.

9. await in catch and finally

Finally, we can happily use the asynchronous programming syntax sugar in exception handling:

private async void Test(){    try    {        await new Task<int>(() =>        {            return 1;        });    }    catch    {        await new Task<int>(() =>        {            return 1;        });    }    finally    {        await new Task<int>(() =>        {            return 1;        });    }}


Finally, I hope Win10 can be popularized quickly so that a large number of. Net programmers can truly use these powerful weapons.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.