in

Deep Dive Into C # 9, Hacker News

Deep Dive Into C # 9, Hacker News


The official planned next C # release is C # 9. You can find in thislinkthe Language Version Planning for C # ·

As shown above, in the list, there are 34 proposals / features are planned for C # 9, but that does not mean that all of those proposals will be released in C # 9.

Which proposal will be released, and in which version? The .Net development team can only answerall these questions. They have the final decision, and they can change their minds all the time, both on proposed features and syntax / semantics of these features.

The most important features planned for C # 9, namely record types, potentially discriminated unions, more pattern matching enhancements, and additional target-typing of existing constructs such as ternary and null -***alescing expressions.

In this article, I will describe Record and Discriminated Unions and the other proposals I will describe them briefly.

!!!!! Important !!!!!

Particularly for both Records and Discriminated Unions, they are not in the final stage. They both still need a lot of work to move from the current strawman proposal syntax to what their final designs will be.

Records

I have been waiting for a long time for this feature. Records are a lightweight type. They are nominally typed, and they might have (methods, properties, operators, etc.), and allow you to compare structural equality! Also, the record properties are read-only by default. Records can be Value Type or Reference Type.

The Proposal in GitHubhere

The most up-to-date proposal for records ishere.

Record from the new proposal

Record would be defined as follows,

  1. publicclass(Point3D) ************************
  2. {
  3. publicintX {get;(set); }
  4. publicintY {get;(set); }
  5. publicintZ {get;(set); }
  6. }

In case: Immutable Type

The proposed solution is a new modifier,initonly, that can be applied to properties and fields,

  1. publicclass(Point3D)
  2. {
  3. public(Initonly)intX {get; }
  4. publicinitonlyintY {get; }
  5. publicinitonlyintZ {get; }
  6. . ..
  7. }

Creating Record

  1. voidDoSomething ()
  2. {
  3. var point3D=newPoint3D ()
  4. {
  5. X=1,
  6. Y=1,
  7. Z=1
  8. };
  9. }

Record from the old proposal

Example, the following record with a primary constructor

  1. dataclassPoint3DintX,int(Y,intZ);

Would be equivalent to,

  1. publicclassDemo
  2. {
  3. publicvoidCreatePoint ()
  4. {)
  5. var p=newPoint3D (1.0, 1.0, 1.0);
  6. }
  7. }

Would be equivalent to,

  1. dataclass(Point3D)
  2. {
  3. publicintX {get; }
  4. publicintY {get; }
  5. publicintZ {get; }
  6. public () *********************** (Point)intx,inty,intz)
  7. {
  8. X=x;
  9. Y=y;
  10. Z=z;
  11. }
  12. publicvoidDeconstruct (outint(X,outintY,(out) ************************intZ)
  13. {
  14. X=this. X;
  15. Y=this. Y;
  16. Z=this. Z;
  17. }
  18. }

The final generation of the above would be

  1. (class) (Point3D)
  2. {
  3. publi CInitonlyintX {get; }
  4. publicinitonlyintY {get; }
  5. publicinitonlyintY {get; }
  6. publicPoint3Dintx,inty,intz)
  7. {
  8. X=x;
  9. Y=y;
  10. Y=z;
  11. }
  12. protectedPoint3D (Point3D other)
  13. :this(other.X, other.Y, other.Z)
  14. {}
  15. [WithConstructor]
  16. publicvirtualPoint With ()=>newPoint (this);
  17. publicvoidDeconstruct ((out)intX,outintY,(out)int (Z)
  18. {
  19. X=this. X;
  20. Y=this. Y;
  21. Z=this. Z;
  22. }
  23. }

Using Records and the With-expression

Records proposal is introduced with the new proposed feature “with-expression”. In programming, an immutable object is an object whose state cannot be modified after it is created. If you want to change the object you have to copied it. The “with” help you to solve the problem, and you can use them together as the following.

  1. publicclassDemo
  2. {
  3. publicvoidDoIt ()
  4. {
  5. var point3D=newPoint3D (){X=1, Y=1, Z=1};
  6. Console.WriteLine (point3D) ;
  7. }
  8. }
  1. var newPoint3D=point3D with {X=42};

The created new point (newPoint3D) just like the existing one (point3D), but with the value of X changed to 42.

This proposal is working very well with pattern matching.

Records in F #

Copy from F # MSDN example, typePoint3D={X: float; Y: float; Z: float}

  1. let evaluatePoint (point: Point3D)=
  2. match point with
  3. {X=0.0; Y=0.0; Z=0.0} ->printfn“Point is at the origin.”
  4. {X=xVal; Y=0.0; Z=0.0} ->printfn“Point is on the x-axis. Value is% f.”xVal
  5. {X=0.0; Y=yVal; Z=0.0} ->printfn“Point is on the y-axis. Value is% f.”yVal
  6. {X=0.0; Y=0.0; Z=zVal} ->printfn“Point is on the z-axis. Value is% f.”zVal
  7. {X=xVal; Y=yVal; Z=zVal} ->printfn“Point is at (% f,% f,% f).”xVal yVal zVal
  8. evaluatePoint {X=0.0; Y=0.0; Z=0.0}
  9. evaluatePoint {X=100. Y=0.0; Z=0.0}
  10. evaluatePoint {X=10 .0; Y=0.0; Z=-1.0}

The output of this code is as follows.

Point is at the origin.

Point is on the x-axis. Value is 100. 000000.

Point is at 10. 000000, 0. 000000, -1. 000000).

Record types are implemented by the compiler, which means you have to meet all of those criteria and can’t get them wrong.

So not only do they save a lot of boilerplate, they eliminate an entire class of potential bugs.

Moreover, this feature existed over a decade in F #, and other languages ​​like (Scala, Kotlin) have a similar concept too.

Examples for other languages ​​that support both constructors and records.

F #

  1. type Greeter (name:(string))=memberthisSayHi ()=printfn“Hi,% s”name

Scala

  1. classGreeter (name: String)
  2. {
  3. def SayHi ()=println (“Hi,” name)
  4. }

(Equality)

Records are compared by structure and not by reference

Example

  1. voidDoSomething ()
  2. {
  3. var point3D1=newPoint3D ()
  4. {[DoesNotReturnIf(bool)]
  5. X=1,
  6. Y=1,
  7. Z=1
  8. };
  9. var point3D2=new(Point3D ()
  10. {
  11. X=1,
  12. Y=1,
  13. Z=1
  14. };
  15. var compareRecords=point3D1==point3D2;
  16. }

Discriminated Union

The term discriminated union (disjoint union) is borrowed from mathematics. A simple example to understand the term.

Consider the associated sets,

A0={(5,0), (6,1)}

(A1={(7,2)}

The discriminated union can then be calculated as follows,

(A 0) *********************** (⊔) A 1={(5,0), (6,1), (7,2)}

As you can see above, the discriminated union is the sum of the associated sets. Discriminated union (disjoint union) is also widely used in the programming languages ​​(especially in FP), which used to sum the existing data types.

Discriminated unions in C # 9

It offers a way to define types that may hold any number of different data types. Their functionality is similar to F # discriminated union.

The official proposalhere.

Discriminated unions are useful for heterogeneous data; data that can have individual cases, with valid and error cases; data that vary in type from one instance to another. Besides, it offers an alternative for small object hierarchies.

F # discriminated union example.

  1. type Person={firstname:string; lastname:string}
  2. type ByteOrBool=Y ofbyteB ofbool
  3. type MixedType=
  4. | P of Person
  5. U of ByteOrBool

  6. let unionRecord=MixedType .P ({firstname=“Bassam”; lastname=“Alugili”});
  7. let unionType1=MixedType U (Btrue);
  8. let unionType2=MixedType.U (Y 86 uy);

C # 9 Discriminated Unions example.

Using C # records, this could possibly be expressed in C #, using a form of union definition syntax, as,

  1. publicclassPerson
  2. {
  3. publicinitonlystringFirstname {get; }
  4. publicinitonlystringLastname {get; }
  5. } ;
  6. (enum)classByteOrBool {BYTEY;(bool)B;}

We need a time that we need is a type that represents all possible integers PLUS all possible Booleans.

In other words, ByteOrBool is the sum type. In our case, the new type is the “sum” of the byte type plus the boolean type. And as in F #, a sum type is called a “discriminated union” type.

  1. (enum)classMixedType
  2. {
  3. Person P;
  4. ByteOrBool U;
  5. }

Constructing a union instance,

  1. var person=newPerson ()
  2. {
  3. Firstna me=”Bassam”;
  4. Lastname=“Alugili”;
  5. };
  6. var unionRecord=newMixedType.P (person);
  7. var unionType1=newMixedType.U (Btrue);
  8. var unionType2=newMixedType.U (Y 86 uy);

Usage of discriminated unions

With Pattern matching

Using the “subtype” names directly and the upcoming match expression. These below examples and are just for demo to make a better understanding for the proposal.

Exception handling like in Java,

  1. try
  2. {
  3. }
  4. (catch)(CommunicationException | SystemException ex)
  5. {
  6. }

As type constraint

  1. publicclassGenericClasswhere T: T1 | T2 | T3

Generic class can be one of those typesT1 or T2 or T3

Typed heterogeneous collections

  1. var crazyCollectionFP=newListint|double|string>{1, 2.3,“Bassam”} ;

Examples from the proposal,

Resultant type of combination of variables / values / expressions of different types through? :, ?? or switch expression combinators.

  1. var result=xswitch{(true)=>“Successful”,false=>0};

The type of result here will be- string | int

If multiple overloads of some method have same implementations, Union type can do the job,

  1. voidlogInput (intinput)=>Console.WriteLine ($“The input is {input}”);
  2. voidlogInput ((long)input)=>Console.WriteLine ($“The input is {input}”);
  3. voidlogInput (floatinput)=>Console.WriteLine ($“The input is {input}”);

Can be changed to

  1. (void) (logInput)int|long|floatinput)=>Console.WriteLine ($“The input is {input } “);

Maybe as return types,

  1. publicint| Exception Method ()
  2. publicclass(None {}
  3. publictypealias Option=T | None;
  4. publictypealias Result=T | Exception;

More Exampleshere.

Enhancing the Common Type Specification

The proposalhere.

Target typed null coalescing (`??`) expression

It is about allowing an implicit conversion from  the null coalescing expression.

(Example) **************************

    (void) (M) Listint>list,uint? U)

  1. {
  2. IEnumerableint>x=list ?? (IEnumerableint>)new[] {1, 2};
  3. var l=u ?? -1u;
  4. }
  5. voidM (Listint>list ,uint? U)
  6. {
  7. () IEnumerableint>X=list ??(new)[] {1, 2};

  8. var l=u ?? -1;
  9. }

Target-typed implicit array creation expression

Introducing the“new ()” (expression.)

The official proposal Examples,

  1. IEnumerablestring,string>>Headers=new[]
  2. {
  3. newKeyValuePairstring,string>“Foo”, foo),
  4. new(KeyValuePairstring,string>() “Bar”, bar),
  5. }

Can be simplified to

  1. IEnumerablestring,string>>Headers=[DoesNotReturnIf(bool)] newKeyValuePair (string),string>[]
  2. {
  3. new(“Foo”, foo),
  4. new(“Bar”, bar),
  5. }

But you still need to repeat the type following the field / property initializer. The closest you can get is something like:

  1. IEnumerable(string),(string)>>Headers=new[]
  2. {
  3. newKeyValuePairstr ing,string>(“Foo”, foo),
  4. new(“Bar”, bar ),
  5. }

For the sake of completeness, I’d suggest to also make new [] a target-t

[Maybe/NotNullWhen(bool)]

  • IEnumerablestring,(string)>>Headers=(new)[]
  • {
  • (new)(“Foo”, foo),
  • new(“Bar”, bar) ,
  • }
  • Target-typed new-expressions

    “var”infers the left side, and this feature allows us to infer the right side.

    Ex ample

    1. Point p=new(x, y);
    2. ConcurrentDictionary>x=new();
    3. Mads example: Point [] ps={new(1, 4), [Disallow/Allow/Maybe/NotNull]new(3, -2),(new)(9, 5)};

    Caller Expression Attribute

    Allows the caller to ‘stringify’ the expressions passed in at a call site. The constructor of the attribute will take a string argument specifying the name of the argument to stringify.

    Example

    1. publicstatic(class)Verify {
    2. public(static)void(InRange)intargument,intlow,inthigh,
    3. [CallerArgumentExpression(“argument”)]stringargumentExpression=null,
    4. [CallerArgumentExpression(“low”)]stringlowExpression=null,
    5. [CallerArgumentExpression(“high”)](string)highExpression=(null)) {
    6. if(Argument
    7. throw(new)ArgumentOutOfRangeException (paramName: argumentExpression, message: $“{argumentExpression} ({argument}) cannot be less than { lowExpression} ({low}). “);
    8. }
    9. if(argument>high ) {)
    10. ThrownewArgumentOutOfRangeException (paramName: argumentExpression, message: $“{argumentExpression} ({argument}) cannot be greater than {highExpression} ({high}). “);
    11. }
    12. }
    13. publicstatic(void) (NotNull) (T argument,)
    14. [CallerArgumentExpression(“argument”)]stringargumentExpression=null
    15. where T:class{
    16. (if)(argument==)null)thrownewArgumentNullException (paramName: argumentExpression);

    17. }
    18. }
    19. Verify.NotNull (array);
    20. “index (-1) cannot be less than 0 (0).”, or
    21. Verify.InRange (index, 0, array.Length – 1);

    Default in deconstruction

    Allows the following syntax(int i, string s)=default ;and(i, s)=default;.

    Example

    1. (intx,stringy)=(default,default);
    2. (intx,stringy)=default;

    Relax ordering of ref and partial modifiers

    Allows the partial keyword before ref in the class definition.

    Example

    1. publicref(partial) ************************struct{}
    2. publicpartialrefstruct{}

    Parameter null-checking

    Allow simplifying the standard null validation on parameters by using a small annotation on parameters. This feature belongs to code enhancing.

    Last meeting noteshere.

    Example

    1. voidDoSomething (stringtxt)
    2. {
    3. if(TXTisnull)
    4. {
    5. thrownewArgumentNullException (nameof (txt));
    6. }
    7. }
    8. voidDoSomething (strin gTxt!)
    9. {)
    10. }

    Skip locals init

    Allow specifying System.Runtime.CompilerServices.SkipLocalsInitAttribute as a way to tell the compiler to not emit localsinit flag. SkipLocalsInitiAttribute is added to CoreCLR.

    The end result of this will be that the locals may not be zero-initialized by the JIT, which is, in most cases, unobservable in C #.

    In addition to that stackalloc data will not be zero-initialized. That is observable but also is the most motivating scenario.

    Lambda discard parameters

    Allow the lambda to have multiple declarations of the parameters named _. In this case, the parameters are “discards” and are not usable inside the lambda.

    Examples

    1. Func zero=(_, _)=>0;
    2. (_ , _)=>1, (int,string)=>1,voidlocal (int,int);

    Attributes on local functions

    The idea is to permit attributes to be part of the declaration of a local function.

    “From discussion in LDM today (4 / 29 / 2019), this would help with async-iterator local functions that want to use [EnumeratorCancellation].

    We should also test other attributes: “

    [DoesNotReturn]

    [DoesNotReturnIf(bool)]

    [Disallow/Allow/Maybe/NotNull]

    [Maybe/NotNullWhen(bool)]

    [Obsolete]

    Basic Example,

    1. staticvoidMain (string[] args)
    2. {
    3. staticboolLocalFunc [NotNull] data)
    4. {
    5. returntrue;
    6. }
    7. }

    The main use case for this feature,

    Another example of using it with EnumeratorCancellation on the CancellationToken parameter of a local function implementing an async iterator, which is common when implementing query operators.

    1. publicstaticIAsyncEnumerable Where () thisIAsyncEnumerable source, Func predicate)
    2. {
    3. if(source==******************** (null))
    4. thrownewArgumentNullException (nameof (source));
    5. if(predicate==null)
    6. ThrownewArgumentNullException (nameof (predicate));
    7. returnCore ();
    8. async IAsyncEnumerableCore ([EnumeratorCancellation] CancellationToken token=default)
    9. {)
    10. (await) *********************** (foreach)(var iteminsource.WithCancellat ion (token))
    11. {
    12. if((Predicate (item)))
    13. {
    14. yieldreturnitem;

    15. }
    16. }
    17. }
    18. }

    Advanced Examplehere.

    Native Ints

    Introduces a new set of native types (nint, nuint) the ‘n ’For native. The design of the new data types is planned to allow a one C # source file to use (naturally- or 64 – bit storage depending on the host platform type and the compilation settings.

    Example

    The native type is depending on the OS,

    1. nint nativeInt=55; take 4 bytes when I compilein32 Bit host.
    2. nint nativeInt=55; take 8 bytes when I compilein64 Bit host with x 64 compilation settings.

    Function pointers

    I remember the term function pointer from C / C . FP is a variable that stores the address of a function that can later be called through that function pointer. Function pointers can be invoked and passed arguments just as in a normal function call.

    The proposalhere.

    One of the new C # candidate features is called Function Pointers. The C # function pointer allows for the declaration of function pointers using the func * syntax. It is similar to the syntax used by delegate declarations.

    (Example)

    1. unsafeclassExample
    2. {
    3. voidExample (Action) int>a,delegate*int,void>f)
    4. {
    5. (a) 42);
    6. (f) 42);
    7. }
    8. }

    Summary

    You have read about the candidates in C # 9 Feature Status, and I have demonstrated them to you.

    C # NEXT feature List status is shown below, which at contains the worklist for C # 9. Only if the candidate features in the “master” branch, that means the feature will be released in thenext version.

    Import: a lot of things are still in discussions. The proposed features and syntax / semantics might be changed, or the feature itself might be changed or removed. Only .NET developers can decide which features will be released in C # 9 and when it will be released. In the next article, I will continue with the proposals.

    When the C # 9 will be released, I will make for you a cheat sheet as in C # 7 and C # 8. You can follow me on GitHub or my home page.

    Brave Browser
    Read More
    Payeer

    What do you think?

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    GIPHY App Key not set. Please check settings

    trekhleb / nano-neuron, Hacker News

    trekhleb / nano-neuron, Hacker News

    I Me Mind, Hacker News

    I Me Mind, Hacker News