Skip to content

Instantly share code, notes, and snippets.

@RajaniCode
Last active August 17, 2024 00:17
Show Gist options
  • Select an option

  • Save RajaniCode/7b8176a8aad8d76bcc2f2cdd23796233 to your computer and use it in GitHub Desktop.

Select an option

Save RajaniCode/7b8176a8aad8d76bcc2f2cdd23796233 to your computer and use it in GitHub Desktop.
CS9
// CS 9
/*
1. Records
2. Init only setters
3. Top-level statements
4. Pattern matching enhancements
5. Native sized integers
6. Function pointers
7. Suppress emitting localsinit flag
8. Target-typed new expressions
9. static anonymous functions
10. Target-typed conditional expressions
11. Covariant return types
12. Extension GetEnumerator support for foreach loops
13. Lambda discard parameters
14. Attributes on local functions
15. Module initializers
16. New features for partial methods
17. Warning wave 5
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
/******************************************************************************/
// 3. Top-level statements
/******************************************************************************/
// Top-level statements must precede namespace and type declarations.
// Warning CS7022: The entry point of the program is global code; ignoring 'Program.Main()' entry point. // Errors will not be ignored though.
Console.WriteLine("3. Top-level statements");
// Console.WriteLine($"Hello, {args[0]}!"); // dotnet run World
// return;
// return 1; // only int
// The top-level statements may contain async expressions. In that case, the synthesized entry point returns a Task, or Task<int>.
Task taskAsync = Task.Run(() => Thread.Sleep(1000)); // Task taskAsync = Task.Run(() => Task.Delay(1000));
Console.WriteLine($"Task Status: {taskAsync.Status}");
Console.WriteLine($"Task IsCompleted: {taskAsync.IsCompleted}");
Console.WriteLine("Thread.Sleep(1000)");
Thread.Sleep(1000);
Console.WriteLine($"Task Status: {taskAsync.Status}");
Console.WriteLine($"Task IsCompleted: {taskAsync.IsCompleted}");
await taskAsync;
Task<int> asyncTask = Task.Run( () => Enumerable.Range(1, new Random().Next(100)).Count( x => x % 2 == 0));
Console.WriteLine($"Task Status: {asyncTask.Status}");
Console.WriteLine($"Task IsCompleted: {asyncTask.IsCompleted}");
Console.WriteLine($"Task Result: {asyncTask.Result}");
Console.WriteLine($"Task Status: {asyncTask.Status}");
Console.WriteLine($"Task IsCompleted: {asyncTask.IsCompleted}");
// return await asyncTask;
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("Environment Properties");
/******************************************************************************/
EnvironmentProperties propertiesEnvironment = new();
propertiesEnvironment.Print();
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("1. Records");
/******************************************************************************/
Console.WriteLine($"record IsValueType: {typeof(R).IsValueType}");
Console.WriteLine($"record new() IsValueType: {new R().GetType().IsValueType}");
Console.WriteLine($"record IsClass: {typeof(R).IsClass}");
RecordType typeRecord = new RecordType();
C c1 = new();
C c2 = new();
c1.x = 1;
c2.x = 2;
Console.WriteLine($"Class Before c1 = c2: c1.x = {c1.x}, 2.x = {c2.x}"); // 1, 2
// c1 = c2.ShallowCopy; // 3, 2
c1 = c2; // 3, 3
c1.x = 3;
Console.WriteLine($"Class After c1 = c2 and c1.x = 3: c1.x = {c1.x}, c2.x = {c2.x}"); // 3, 3
S s1 = new();
S s2 = new();
s1.x = 1;
s2.x = 2;
Console.WriteLine($"Structure Before s1 = s2: s1.x = {s1.x}, s2.x = {s2.x}"); // 1, 2
s1 = s2.ShallowCopy; // 3, 2
// s1 = s2; // 3, 2
s1.x = 3;
Console.WriteLine($"Structure After s1 = s2 and s1.x = 3: 1.x = {s1.x}, s2.x = {s2.x}"); // 3, 2
R r1 = new();
R r2 = new();
r1.x = 1;
r2.x = 2;
Console.WriteLine($"Record Before r1 = r2: r1.x = {r1.x}, r2.x = {r2.x}"); // 1, 2
// r1 = r2.ShallowCopy; // 3, 2
r1 = r2; // 3, 3
r1.x = 3;
Console.WriteLine($"Record After r1 = r2 and r1.x = 3: r1.x = {r1.x}, r2.x = {r2.x}"); // 3, 3
var veh = new Vehicle("Boeing", "Dreamliner");
var air = new Aircraft("Boeing", "Dreamliner", 787);
// Equality is value-based
Console.WriteLine("Equality is value-based");
Console.WriteLine(veh == air); // False
Console.WriteLine(veh is Aircraft a); // False
Console.WriteLine(air is Vehicle c); // True
// Print record object ToString()
Console.WriteLine(air.ToString());
// Deconstruct method for positional records
Console.WriteLine("Deconstruct method for positional records");
var (real, nick) = veh;
Console.WriteLine(real);
Console.WriteLine(nick);
var (_, _, acn) = air;
Console.WriteLine(acn);
// Record with-expressions
Console.WriteLine("Record with-expressions");
Vehicle airbusDreamliner = veh with { RealName = "Airbus" };
Console.WriteLine(airbusDreamliner.ToString());
Vehicle boeingSuperjumbo = veh with { Nickname = "Superjumbo" };
Console.WriteLine(boeingSuperjumbo.ToString());
Vehicle airbusSuperjumbo = veh with { RealName = "Airbus", Nickname = "Superjumbo" };
Console.WriteLine(airbusSuperjumbo.ToString());
Vehicle airbusIndustrie = air with { RealName = "Airbus", Nickname = "Superjumbo", AircraftClassificationNumber = 380 };
Console.WriteLine(airbusIndustrie.ToString());
Console.WriteLine(new Games("Paris 2024").ToString());
Console.WriteLine(new Olympics("Los Angeles 2028").ToString());
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("2. Init only setters");
/******************************************************************************/
// Property initializer syntax to set the values, while still preserving the immutability
var metrics = new Analytics
{
Browser = "Chromium",
Sessions = 3000,
PagesPerSession = 3.5F,
};
// metrics = metrics with { Browser = "Chrome" }; // record type
// Changing a property after initialization is an error by assigning to an init-only property outside of initialization
// The construction phase effectively ends after all initialization, including property initializers and with-expressions (record type) have completed.
// Error CS8852: Init-only property or indexer 'Analytics.Browser' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.
// metrics.Browser = "Edge";
Console.WriteLine(metrics.ToString());
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("4. Pattern matching enhancements");
/******************************************************************************/
Console.WriteLine('1'.IsNumeric()); // True
Console.WriteLine('a'.IsNumeric()); // False
// Alternatively, with optional parentheses to make it clear that and has higher precedence than or
Console.WriteLine('a'.IsAlphaNumeric()); // True
Console.WriteLine('1'.IsAlphaNumeric()); // True
Console.WriteLine('~'.IsAlphaNumeric()); // False
var isNot = string.Empty;
if (isNot is not null)
{
Console.WriteLine("is not null");
}
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("5. Native sized integers");
/******************************************************************************/
nint native = nint.MinValue;
nuint unsignedNative = nuint.MaxValue;
Console.WriteLine($"nint.MinValue = {native}");
Console.WriteLine($"nuint.MaxValue = {unsignedNative}");
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("6. Function pointers");
/******************************************************************************/
FunctionPointers funPointers = new();
funPointers.Print();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("7. Suppress emitting localsinit flag");
/******************************************************************************/
Suppresslocalsinit suppress = new();
suppress.StackAllocatedMemoryBlock();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("8. Target-typed new expressions");
/******************************************************************************/
TargetTypedNewExpressions expressionsTargetTyped = new();
var metric = expressionsTargetTyped.MetricsFor("Chrome", new());
// Combine target-typed new with init only properties to initialize a new object
Dimension city = new() { Location = "Scientia" };
Console.WriteLine(city.Location);
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("9. static anonymous functions");
/******************************************************************************/
StaticAnonymousFunctions anonymousFunctionsStatic = new();
anonymousFunctionsStatic.Anonymous();
anonymousFunctionsStatic.Act(anonymousFunctionsStatic.Fun(5));
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("10. Target-typed conditional expressions");
/******************************************************************************/
var rand = new Random();
var condition = rand.Next(0, 99) > 50;
// 10. Target-typed conditional expressions
// Conditional expressions that previously required casts or wouldn’t compile now just work
// int? nullable = condition ? 1 : (int?)null;
int? nullable = condition ? 1 : null;
Console.WriteLine($"{condition} {nullable}");
// Type of conditional expression cannot be determined because there is no implicit conversion between 'int' and '<null>'
// var implicitNullable = condition ? 1 : null;
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("11. Covariant return types");
/******************************************************************************/
B b = new B();
b.VirtualMethodA();
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("12. Extension GetEnumerator support for foreach loops");
/******************************************************************************/
IEnumerator<int> enumerator = Enumerable.Range(0, 10).GetEnumerator();
// Pre-CS 9
/*
while(enumerator.MoveNext())
{
Console.Write(enumerator.Current + " ");
}
*/
// Pre-CS 9 foreach statement cannot operate on variables of type 'IEnumerator<int>' because 'IEnumerator<int>' does not contain a public instance
// CS 9 foreach loop recognizes extension method GetEnumerator
foreach (var item in enumerator)
{
Console.Write(item + " ");
}
Console.WriteLine();
async IAsyncEnumerator<int> GetAsyncEnumerator()
{
yield return 0;
await Task.Delay(1);
yield return 1;
}
// CS 9 foreach recognizes async extension method
IAsyncEnumerator<int> enumeratorAsync = GetAsyncEnumerator();
await foreach (var item in enumeratorAsync)
{
Console.Write(item + " ");
}
Console.WriteLine();
// CS 9 Iterate on ValueTuple using a foreach
foreach (var item in (2, 3, 4))
{
Console.Write(item + " ");
}
Console.WriteLine();
// CS 9 Iterate on Tuple using a foreach
foreach (var item in Tuple.Create(4, 5, 6))
{
Console.Write(item + " ");
}
Console.WriteLine("\n");
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("13. Lambda discard parameters");
/******************************************************************************/
LambdaDiscardParameters DiscardParametersLambda = new();
Console.WriteLine(DiscardParametersLambda.Constant(null, null));
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("14. Attributes on local functions");
/******************************************************************************/
AttributesOnLocalFunctions localFunctionsAttributes = new();
localFunctionsAttributes.Function();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("16. New features for partial methods");
/******************************************************************************/
// CS 9 The two features added for code generators are extensions to partial method syntax, and module initializers
int outResult;
PartialMethods partialMethod = new();
partialMethod.PartialMethod(out outResult);
Console.WriteLine($"partial method out parameter:{outResult}");
Console.WriteLine();
/******************************************************************************/
/******************************************************************************/
Console.WriteLine("17. Warning wave 5");
/******************************************************************************/
WarningWave5 Wave5Warning = new();
/******************************************************************************/
/******************************************************************************/
// 1. Records
/******************************************************************************/
/*
public record Vehicle
{
public string RealName { get; }
public string Nickname { get; }
public Vehicle(string real, string nick) => (RealName, Nickname) = (real, nick);
}
// Inherit record
// Records may only inherit from object or another record
public record Car : Vehicle
{
public string Brand { get; }
public Car(string real, string nick, string tagline) : base(real, nick) => Brand = tagline;
}
// Can be sealed
public sealed record Aircraft : Vehicle
{
public int AircraftClassificationNumber { get; }
public Aircraft(string real, string nick, int acn) : base(real, nick) => AircraftClassificationNumber = acn;
}
*/
// Positional records
public record Vehicle(string RealName, string Nickname);
public record Car(string RealName, string Nickname, string Brand) : Vehicle(RealName, Nickname);
public sealed record Aircraft(string RealName, string Nickname, int AircraftClassificationNumber) : Vehicle(RealName, Nickname);
// Anamoly?
record WithoutCurlyBraces;
// Can have destructor
public record RecordType
{
public RecordType()
{
Console.WriteLine("record constructor");
}
// record destructor
/*
// The programmer has no control over when the finalizer is called; the garbage collector decides when to call it.
// The garbage collector checks for objects that are no longer being used by the application.
// If it considers an object eligible for finalization, it calls the finalizer (if any) and reclaims the memory used to store the object.
// It's possible to force garbage collection by calling Collect, but most of the time, this call should be avoided because it may create performance issues.
*/
~RecordType() => Console.WriteLine("The finalizer is executing.");
}
interface IRecordInterface { }
// Default internal
// Can be generic
// Can be abstract
// Can only inherit from object or another record
// Can implement interface
// Can have static constructor
// Cannot be static
abstract record AssertRecord<T> : object, IRecordInterface
{
static AssertRecord() { }
public abstract void AbstractMethod();
}
// Add a body and include any additional methods as well to record
public record Games(string Name)
{
public void Play() => Console.WriteLine("Good sport!");
}
public record Olympics(string Name) : Games(Name)
{
public void WinMedals() => Console.WriteLine("Bring laurels!");
public override string ToString()
{
StringBuilder s = new();
// CS 9 record base
base.PrintMembers(s);
return $"Olympics {{ {s.ToString()} }}";
}
}
class C
{
public int x = 0;
public C ShallowCopy => (C)MemberwiseClone();
}
struct S
{
public int x;
public S ShallowCopy => (S)MemberwiseClone();
}
record R
{
public int x;
public R ShallowCopy => (R)MemberwiseClone();
}
/******************************************************************************/
/******************************************************************************/
// 2. Init only setters
/******************************************************************************/
// record Analytics
struct Analytics
{
// init accessors instead of set accessors for properties and indexers
// public string Browser { get; set; }
public string Browser { get; init; }
public int Sessions { get; init; }
public float PagesPerSession { get; init; }
public override string ToString() => $"Browser = {Browser}, Sessions = {Sessions}, with {PagesPerSession} pages/session.";
}
/******************************************************************************/
/******************************************************************************/
// 4. Pattern matching enhancements
/******************************************************************************/
static class ExtensionPatternMatchingEnhancements
{
public static bool IsNumeric(this char c) => c is >= '0' and <= '9';
public static bool IsAlphaNumeric(this char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or (>= '0' and <= '9');
}
/******************************************************************************/
/******************************************************************************/
// 6. Function pointers
/******************************************************************************/
// <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
unsafe delegate char* UnsafeDelegate(char *c);
// Only for reference/analogy
/*
delegate int FunctionPointer(string s);
// delegate FunctionPointer FunPointer(FunctionPointer f);
// unsafe delegate delegate*<string, int> FunPointer(delegate* managed<string, int> f);
unsafe delegate delegate*<char*, int> FunPointer(delegate* managed<char*, int> f);
// unsafe delegate delegate* unmanaged<string, void> UnmanagedDelegate();
unsafe delegate delegate* unmanaged<char*, void> UnmanagedDelegate();
*/
class FunctionPointers
{
// Unsafe delegate need not refer static method
// public static char* UnsafeDelegateMethod(char *c) => c;
unsafe public char* UnsafeDelegateMethod(char *c) => c;
// Only static methods for function pointers delegate*
public static void StaticMethod() => Console.WriteLine("Function pointer to static method");
public static void StaticMethod(string s) => Console.WriteLine($"Function pointer to static method: {s}");
public static void StaticMethod(int i) => Console.WriteLine($"Function pointer to static method: {i}");
// Only static methods for managed and unmanaged function pointers delegate*
public static void ManagedStaticMethod(string s) => Console.WriteLine($"Function pointer: {s}");
// unsafe public static void UnManagedStaticMethod(int i) => Console.WriteLine($"Function pointer: {i}");
unsafe public static void UnManagedStaticMethod(char *c) => Console.WriteLine($"Function pointer: {Marshal.PtrToStringAnsi((IntPtr)c)}");
// Only static methods for function pointer equivalents delegate*
public static int Function(string s) { Console.WriteLine($"Function pointer: {s}"); return default(int); }
unsafe public static delegate*<string, int> Fun(delegate* managed<string, int> f) => default(delegate*<string, int>);
unsafe public void Print()
{
UnsafeDelegate delUnsafe = UnsafeDelegateMethod;
// Method groups will now be allowed as arguments to an address-of expression
delegate*<void> delStaticMethod = &StaticMethod; // StaticMethod()
// last parameterized type is return type
delegate*<string, void> delStaticMethods = &StaticMethod; // StaticMethod(string s)
delegate*<int, void> delStaticMethodi = &StaticMethod; // StaticMethod(int i)
// Error: ambiguous conversion from method group StaticMethod to "void*"
// void* v = &StaticMethod;
delegate* managed<string, void> delManaged = &ManagedStaticMethod;
// delegate* unmanaged<int, void> delUnmanaged = (delegate* unmanaged<int, void>)(delegate* <int, void>)&UnManagedStaticMethod;
delegate* unmanaged<char*, void> delUnmanaged = (delegate* unmanaged<char*, void>)(delegate*<char*, void>)&UnManagedStaticMethod;
// Function pointer equivalent without calling convention
delegate*<string, int> delEquivalent = &Function;
delegate*<delegate* managed<string, int>, delegate*<string, int>> delEquivalentManaged = &Fun;
// Function pointer equivalent with calling convention
delegate* managed<string, int> delManagedEqui = &Function;
delegate*<delegate* managed<string, int>, delegate*<string, int>> delEquiManaged = &Fun;
string marshal = "Marshal: unsafe delegate can refer non-static method";
IntPtr iptrmarshal = Marshal.StringToHGlobalAnsi(marshal);
char* cptrmarshal = (char*)iptrmarshal.ToPointer();
char* cpmarshal = delUnsafe(cptrmarshal);
Console.WriteLine(Marshal.PtrToStringAnsi((IntPtr)cpmarshal));
delStaticMethod();
delStaticMethods("Hello, World!");
delStaticMethodi(1);
delManaged("delegate managed");
// delUnmanaged(1);
string marsh = "Marshal: delegate unmanaged";
IntPtr iptrmarsh = Marshal.StringToHGlobalAnsi(marsh);
char* cptrmarsh = (char*)iptrmarsh.ToPointer();
delUnmanaged(cptrmarsh);
Console.WriteLine($"delegate equivalent return: {delEquivalent("delegate equivalent")}");
/*
delegate*<string, int> delEquivalentManagedRoundtrip = delEquivalentManaged(delEquivalent);
delEquivalentManagedRoundtrip = &Function;
Console.WriteLine($"delegate equivalent managed round trip return: {delEquivalentManagedRoundtrip("delegate equivalent managed round trip")}");
*/
delEquivalent = delEquivalentManaged(delEquivalent);
delEquivalent = &Function;
Console.WriteLine($"delegate equivalent managed round trip return: {delEquivalent("delegate equivalent managed round trip")}");
Console.WriteLine($"delegate managed equi return: {delManagedEqui("delegate managed equi")}");
/*
delegate*<string, int> delEquiManagedRoundtrip = delEquiManaged(delManagedEqui);
delEquiManagedRoundtrip = &Function;
Console.WriteLine($"delegate equi managed round trip return: {delEquiManagedRoundtrip("delegate equi managed round trip")}");
*/
delManagedEqui = delEquiManaged(delManagedEqui);
delManagedEqui = &Function;
Console.WriteLine($"delegate equi managed round trip return: {delManagedEqui("delegate equi managed round trip")}");
Console.WriteLine();
}
}
/******************************************************************************/
/******************************************************************************/
// 7. Suppress emitting localsinit flag
/******************************************************************************/
class Suppresslocalsinit
{
// CS 9 Add the SkipLocalsInitAttribute to instruct the compiler not to emit the localsinit flag
// As this flag instructs the CLR to zero-initialize all local variables
// <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
[SkipLocalsInitAttribute]
public void StackAllocatedMemoryBlock()
{
Console.Write("SkipLocalsInitAttribute for stackalloc: ");
int length = 3;
Span<int> numbers = stackalloc int[length];
for (var i = 0; i < length; i++)
{
numbers[i] = i;
// Console.Write($"{numbers[i]}{(char)32}");
}
Console.WriteLine(string.Join(", ", numbers.ToArray()));
Console.WriteLine();
}
}
/******************************************************************************/
/******************************************************************************/
// 8. Target-typed new expressions
/******************************************************************************/
class AnalyticsOptions { }
class Dimension
{
// Init only setters or properties
public string Location { get; init; }
}
class TargetTypedNewExpressions
{
// 8. Target-typed new expressions
// CS 9 Omit the type in a new expression when the created object's type is already known
private List<Analytics> metrics = new();
// Return an instance created by the default constructor using a return new(); statement
public Analytics Instance()
{
return new();
}
// 8. Target-typed new expressions
// Target-typed new can also be used when you need to create a new object to pass as an argument to a method
// public Analytics MetricsFor(string browser, AnalyticsOptions options) => new();
public Analytics MetricsFor(string browser, AnalyticsOptions options) => Instance();
}
/******************************************************************************/
/******************************************************************************/
// 9. static anonymous functions
/******************************************************************************/
delegate void Del();
class StaticAnonymousFunctions
{
// 9. static anonymous functions
// CS 9 static modifier for lambda expressions or anonymous methods
public Del Anonymous = static () => Console.WriteLine("=> static anonymous functions"); // Del anonymous = static delegate { Console.WriteLine("9. static anonymous functions"); };
public Func<int, int> Fun = static x => x * x;
public Action<int> Act = static (int i) => Console.WriteLine($"=> static action: {i}"); // Action<int> act = static delegate (int i) { Console.WriteLine($"static action: {i}"); };
}
/******************************************************************************/
/******************************************************************************/
// 11. Covariant return types
/******************************************************************************/
class A
{
public virtual A VirtualMethodA() => new();
}
class B : A
{
// 11. Covariant return types
// CS 9 Overridden virtual function can return a type derived from the return type declared in the base class method
public override B VirtualMethodA() => new();
// public override Covariant VirtualMethodA() => new();
}
// class Covariant : A { }
class Covariant : B { }
/******************************************************************************/
/******************************************************************************/
// 12. Extension GetEnumerator support for foreach loops
/******************************************************************************/
static class ExtensionGetEnumeratorSupportForForeachLoops
{
// CS 9 foreach loop will recognize and use an extension method GetEnumerator
public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> enumerator) => enumerator;
public static IAsyncEnumerator<T> GetAsyncEnumerator<T>(this IAsyncEnumerator<T> enumerator) => enumerator;
public static IEnumerator<object> GetEnumerator<T1, T2, T3>(this ValueTuple<T1, T2, T3> tuple)
{
yield return tuple.Item1;
yield return tuple.Item2;
}
public static IEnumerator<object> GetEnumerator<T1, T2, T3>(this Tuple<T1, T2, T3> tuple)
{
yield return tuple.Item1;
yield return tuple.Item2;
}
}
/******************************************************************************/
/******************************************************************************/
// 13. Lambda discard parameters
/******************************************************************************/
class LambdaDiscardParameters
{
// 13. Lambda discard parameters
// CS 9 discards for specifying two or more input parameters of a lambda expression that aren't used in the expression
public Func<string, string, string> Constant = static (_, _) => "13. Lambda discard parameters (9. static anonymous functions)";
}
/******************************************************************************/
/******************************************************************************/
// 14. Attributes on local functions
/******************************************************************************/
class AttributesOnLocalFunctions
{
public void Function()
{
Console.WriteLine("AllowNull");
Console.WriteLine(AllowNullLocalFunction(null));
Console.WriteLine("DisallowNull: warning CS8625, warning CS8603");
#nullable enable // Or //#nullable enable warnings // warning CS8625: Cannot convert null literal to non-nullable reference type.
Console.WriteLine(DisallowNullLocalFunction(null));
#nullable disable // Or //#nullable disable warnings
/*
You can use the following modifiers with a local function:
async
unsafe
static (in C# 8.0 and later). A static local function can't capture local variables or instance state.
extern (in C# 9.0 and later). An external local function must be static.
*/
// 14. Attributes on local functions
// CS 9 Nullable attribute annotations to local functions
Console.WriteLine("14. Attributes on local functions");
[return: MaybeNull] string AllowNullLocalFunction([AllowNull] string allowNull) => null;
#nullable enable // Or //#nullable enable warnings // warning CS8603: Possible null reference return // [return: NotNull]
string DisallowNullLocalFunction([DisallowNull] string disallowNull) => null;
#nullable disable // Or //#nullable disable warnings
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
try
{
#nullable enable
string? currentFilePathAndNameWin32NT = new StackTrace(true).GetFrame(0)?.GetFileName();
string? currentDirectoryWin32NT = Path.GetDirectoryName(currentFilePathAndNameWin32NT);
#nullable disable
string sourceDirectoryWin32NT = currentDirectoryWin32NT + @"\GCCWin32NT\C";
string targetDirectoryWin32NT = Directory.GetCurrentDirectory();
// Console.WriteLine($"Current Directory: {currentDirectoryWin32NT}");
// Console.WriteLine($"Source Directory: {sourceDirectoryWin32NT}");
// Console.WriteLine($"Target Directory: {targetDirectoryWin32NT}");
const string fileNameWin32NT = "msys64-mingw64-gcc.dll";
Console.WriteLine($"C Library File Name: {fileNameWin32NT}");
File.Copy(Path.Combine(sourceDirectoryWin32NT, fileNameWin32NT), Path.Combine(targetDirectoryWin32NT, fileNameWin32NT), true);
// CS 9 extern modifier for local functions
Console.WriteLine("static extern in local function");
[DllImport(fileNameWin32NT, CharSet = CharSet.Unicode)]
static extern int c_code();
Console.WriteLine($"C Code: Call Void Fuction: c_code(): {c_code()}");
Console.WriteLine();
}
catch (IOException ex)
{
// Console.WriteLine($"ArgumentException StackTrace: {ex.StackTrace}");
Console.WriteLine($"ArgumentException Message: {ex.Message}");
}
catch (UnauthorizedAccessException ex)
{
// Console.WriteLine($"ArgumentException StackTrace: {ex.StackTrace}");
Console.WriteLine($"ArgumentException Message: {ex.Message}");
}
catch (Exception ex)
{
// Console.WriteLine($"ArgumentException StackTrace: {ex.StackTrace}");
Console.WriteLine($"ArgumentException Message: {ex.Message}");
}
}
if (Environment.OSVersion.Platform == PlatformID.Unix)
{
try
{
#nullable enable
string? currentFilePathAndName = new StackTrace(true).GetFrame(0)?.GetFileName();
string? currentDirectory = Path.GetDirectoryName(currentFilePathAndName);
#nullable disable
string sourceDirectory = currentDirectory + "/GCC/C_x86_64";
string targetDirectory = Directory.GetCurrentDirectory();
// Console.WriteLine($"Current Directory: {currentDirectory}");
// Console.WriteLine($"Source Directory: {sourceDirectory}");
// Console.WriteLine($"Target Directory: {targetDirectory}");
const string fileName = "msys64-mingw64-gcc.so";
Console.WriteLine($"C Library File Name: {fileName}");
File.Copy(Path.Combine(sourceDirectory, fileName), Path.Combine(targetDirectory, fileName), true);
// CS 9 extern modifier for local functions
Console.WriteLine("static extern in local function");
[DllImport(fileName, CharSet = CharSet.Unicode)]
static extern int c_code(int number);
Console.WriteLine($"C Code: Multiply By 10: By Passing Argument (int.MaxValue / 10) For Parameter (number) To c_code(int number): {c_code(int.MaxValue / 10)}");
Console.WriteLine();
}
catch (IOException ex)
{
// Console.WriteLine($"ArgumentException StackTrace: {ex.StackTrace}");
Console.WriteLine($"ArgumentException Message: {ex.Message}");
}
catch (UnauthorizedAccessException ex)
{
// Console.WriteLine($"ArgumentException StackTrace: {ex.StackTrace}");
Console.WriteLine($"ArgumentException Message: {ex.Message}");
}
catch (Exception ex)
{
// Console.WriteLine($"ArgumentException StackTrace: {ex.StackTrace}");
Console.WriteLine($"ArgumentException Message: {ex.Message}");
}
}
}
}
/******************************************************************************/
/******************************************************************************/
// 15. Module initializers
/******************************************************************************/
class ModuleInitializers
{
/*
Must have ModuleInitializerAttribute attribute
Must be static
Must be non-virtual
Must be parameterless
Must return void
Must not be a generic method
Must not be contained in a generic class
Must be accessible from the containing module // Will be called by the runtime when the assembly loads
*/
// ModuleInitializer called without invoking
[ModuleInitializerAttribute]
public static void ModuleInitializer()
{
Console.WriteLine("15. Module initializers");
Console.WriteLine("ModuleInitializerAttribute, static, non-virtual, parameterless, and void");
Console.WriteLine("Will be called by the runtime when the assembly loads");
Console.WriteLine();
}
}
/******************************************************************************/
/******************************************************************************/
// 16. New features for partial methods
/******************************************************************************/
partial class PartialMethods
{
// Defining declaration for implementing declaration of partial method
public partial PartialMethods PartialMethod(out int o);
}
partial class PartialMethods
{
// Pre-CS9 partial methods are private but can't specify an access modifier, have a void return, and can't have out parameters
// CS 9 removes these restrictions, but requires that partial method declarations have an implementation
// Partial method without an access modifier follow the old rules
// If the partial method includes the private access modifier, the new rules govern that partial method
public partial PartialMethods PartialMethod(out int o)
{
o = 1;
return new();
}
}
/******************************************************************************/
/******************************************************************************/
// 17. Warning wave 5
/******************************************************************************/
// Comment out this for Warning wave 5
// #pragma warning disable CS7023, CS8073, CS8848, CS8897, CS8898, CS0219
class WarningWave5
{
static class StaticClass
{
public static void Thing() { }
}
private void M(object o)
{
// CS7023 - A static type is used in an 'is' or 'as' expression.
// Warning CS7023: The second operand of an 'is' or 'as' operator may not be static type 'WarningWave5.StaticClass'
// To correct this warning, remove the test and remove any code executed only if the test succeeded.
if (o is StaticClass)
{
Console.WriteLine("Can't happen");
}
else
{
Console.WriteLine("o is not an instance of a static class");
}
}
class Program
{
public static void M(S s)
{
// CS8073 - The result of the expression is always 'false' (or 'true').
// To fix this error, remove the null check and code that would execute if the object is null.
if (s == null) { } // Warning CS8073: The result of the expression is always 'false' since a value of type 'WarningWave5.S' is never equal to 'null' of type 'WarningWave5.S?'
if (s != null) { } // Warning CS8073: The result of the expression is always 'true' since a value of type 'WarningWave5.S' is never equal to 'null' of type 'WarningWave5.S?'
}
}
struct S
{
public static bool operator ==(S s1, S s2) => s1.Equals(s2);
public static bool operator !=(S s1, S s2) => !s1.Equals(s2);
#nullable enable // Or //#nullable enable warnings // Warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
public override bool Equals(object? other)
#nullable disable // Or //#nullable disable warnings
{
// Implementation elided
return false;
}
public override int GetHashCode() => 0;
// Other details elided...
}
class Src { }
class Src2 { }
private void CS8848()
{
dynamic dyna = new object();
var source = dyna;
bool b = true;
// Warning CS8848: Operator 'from' cannot be used here due to precedence. Use parentheses to disambiguate.
// To fix this warning, put parentheses around the query expression
// var a = b && (from c in source select c); // Error commented out
var indexes = dyna;
int[] array = { 1, 2, 3, 4, 5, 6, 7 };
// Warning CS8848: Operator 'from' cannot be used here due to precedence. Use parentheses to disambiguate.
// To fix this warning, put parentheses around the query expression
// var range = array[0..(from c in indexes select c)]; // Error commented out
}
/*
// Members must be fully assigned, use of unassigned variable (CS8880, CS8881, CS8882, CS8883, CS8884, CS8885, CS8886, CS8887)
// Several warnings improve the definite assignment analysis for struct types declared in imported assemblies.
// All these new warnings are generated when a struct in an imported assembly includes an inaccessible field (usually a private field) of a reference type:
*/
/*
public struct Struct
{
// Error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater.
private string data = String.Empty;
// Error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater.
public Struct() { }
}
public struct DefiniteAssignmentNoWarnings
{
// CS8880
public Struct Property { get; }
// CS8881
private Struct field;
// CS8882
public void Method(out Struct s)
{
s = default;
}
public DefiniteAssignmentNoWarnings(int dummy)
{
Property = default;
field = default;
// CS8883
Struct v2 = Property;
// CS8884
Struct v3 = field;
// CS8885:
DefiniteAssignmentNoWarnings p2 = this;
}
public static void Method2(out Struct s1)
{
// CS8886
s1 = default;
var s2 = s1;
}
public static void UseLocalStruct()
{
Struct r1 = default;
var r2 = r1;
}
}
*/
public static class Utilities { }
public interface IUtility
{
// Warning CS8897: 'WarningWave5.Utilities': static types cannot be used as parameters
// To fix this warning, change the parameter type or remove the method.
public void SetUtility(Utilities u);
// Warning CS8898: 'WarningWave5.Utilities': static types cannot be used as return types
// To fix this warning, change the return type or remove the method.
public Utilities GetUtility();
}
}
/******************************************************************************/
/******************************************************************************/
// Environment Properties
/******************************************************************************/
class EnvironmentProperties
{
public void Print()
{
Console.WriteLine($"Environment.OSVersion: {Environment.OSVersion}");
Console.WriteLine($"Environment.OSVersion.Platform: {Environment.OSVersion.Platform}");
Console.WriteLine($"Environment.OSVersion.Version: {Environment.OSVersion.Version}");
Console.WriteLine($"Environment.OSVersion.VersionString: {Environment.OSVersion.VersionString}");
Console.WriteLine($"Environment.OSVersion.Version.Major: {Environment.OSVersion.Version.Major}");
Console.WriteLine($"Environment.OSVersion.Version.Minor: {Environment.OSVersion.Version.Minor}");
Console.WriteLine($"Environment.OSVersion.Platform == System.PlatformID.Win32NT: {Environment.OSVersion.Platform == System.PlatformID.Win32NT}");
Console.WriteLine($"Environment.OSVersion.Platform == System.PlatformID.Unix: {Environment.OSVersion.Platform == System.PlatformID.Unix}");
// Mono JIT compiler version 6.12.0.206 'PlatformID' does not contain a definition for 'Other'
// Console.WriteLine($"Environment.OSVersion.Platform == System.PlatformID.Other: {Environment.OSVersion.Platform == System.PlatformID.Other}");
// System.PlatformID.Win32NT
// Console.WriteLine($"Environment.OSVersion.ServicePack: {Environment.OSVersion.ServicePack}");
// Environment.Version property returns the .NET runtime version for .NET 5+ and .NET Core 3.x
// Not recommend for .NET Framework 4.5+
Console.WriteLine($"Environment.Version: {Environment.Version}");
// <-- Keep this information secure! -->
// Console.WriteLine($"Environment.UserName: {Environment.UserName}");
// <-- Keep this information secure! -->
// Console.WriteLine($"Environment.MachineName: {Environment.MachineName}");
// <-- Keep this information secure! -->
// Console.WriteLine($"Environment.UserDomainName: {Environment.UserDomainName}");
Console.WriteLine($"Environment.Is64BitOperatingSystem: {Environment.Is64BitOperatingSystem}");
Console.WriteLine($"Environment.Is64BitProcess: {Environment.Is64BitProcess}");
// <-- Keep this information secure! -->
// Console.WriteLine("CurrentDirectory: {0}", Environment.CurrentDirectory);
// <-- Keep this information secure! -->
// Console.WriteLine("SystemDirectory: {0}", Environment.SystemDirectory);
// RuntimeInformation.FrameworkDescription property gets the name of the .NET installation on which an app is running
// .NET 5+ and .NET Core 3.x // .NET Framework 4.7.1+ // Mono 5.10.1+
Console.WriteLine($"System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");
Console.WriteLine($"System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture: {System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture}");
Console.WriteLine($"System.Runtime.InteropServices.RuntimeInformation.OSArchitecture: {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture}");
Console.WriteLine($"System.Runtime.InteropServices.RuntimeInformation.OSDescription): {System.Runtime.InteropServices.RuntimeInformation.OSDescription}");
// Mono JIT compiler version 6.12.0.206 'RuntimeInformation' does not contain a definition for 'RuntimeIdentifier'
// Console.WriteLine($"System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier: {System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier}");
// <-- Keep this information secure! -->
#if comments
Console.WriteLine("Environment Variables:");
foreach (System.Collections.DictionaryEntry de in Environment.GetEnvironmentVariables())
{
Console.WriteLine("{0} = {1}", de.Key, de.Value);
}
#endif
}
}
/******************************************************************************/
// msys64-mingw64-gcc.dll
/*
$ mkdir -p "GCCWin32NT/C"
$ vim GCCWin32NT/C/msys64-mingw64-gcc.c
$ cat GCCWin32NT/C/msys64-mingw64-gcc.c
#include <stdio.h>
int
c_code(void) {
printf("C!\n");
}
$ gcc -c msys64-mingw64-gcc.c
$ gcc -shared -o msys64-mingw64-gcc.dll msys64-mingw64-gcc.o
*/
// msys64-mingw64-gcc.so
/*
% mkdir -p "GCC/C_x86_64"
% nano GCC/C_x86_64/msys64-mingw64-gcc.c
% cat GCC/C_x86_64/msys64-mingw64-gcc.c
#include <stdio.h>
int
c_code(int number) {
return number * 10;
}
/*
% sw_vers
ProductName: macOS
ProductVersion: 14.4.1
BuildVersion: 23E224
% arch
arm64
% arch -x86_64 zsh
% arch
i386
% gcc --version
Apple clang version 15.0.0 (clang-1500.3.9.4)
Target: x86_64-apple-darwin23.4.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
% gcc -c -fPIC msys64-mingw64-gcc.c
% gcc -shared -o msys64-mingw64-gcc.so msys64-mingw64-gcc.o
*/
// NB
// CS 8 Nullable reference type
// csc/mcs #nullable enable // #nullable disable
// dotnet // <Nullable>enable</Nullable> in .csproj
// % dotnet run --configuration Debug
// Or
// % dotnet run --configuration Release
// Output
/*
15. Module initializers
ModuleInitializerAttribute, static, non-virtual, parameterless, and void
Will be called by the runtime when the assembly loads
3. Top-level statements
Task Status: WaitingToRun
Task IsCompleted: False
Thread.Sleep(1000)
Task Status: RanToCompletion
Task IsCompleted: True
Task Status: WaitingToRun
Task IsCompleted: False
Task Result: 18
Task Status: RanToCompletion
Task IsCompleted: True
Environment Properties
Environment.OSVersion: Unix 14.4.1
Environment.OSVersion.Platform: Unix
Environment.OSVersion.Version: 14.4.1
Environment.OSVersion.VersionString: Unix 14.4.1
Environment.OSVersion.Version.Major: 14
Environment.OSVersion.Version.Minor: 4
Environment.OSVersion.Platform == System.PlatformID.Win32NT: False
Environment.OSVersion.Platform == System.PlatformID.Unix: True
Environment.Version: 5.0.17
Environment.Is64BitOperatingSystem: True
Environment.Is64BitProcess: True
System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription: .NET 5.0.17
System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture: X64
System.Runtime.InteropServices.RuntimeInformation.OSArchitecture: X64
System.Runtime.InteropServices.RuntimeInformation.OSDescription): Darwin 23.4.0 Darwin Kernel Version 23.4.0: Fri Mar 15 00:10:42 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6000
1. Records
record IsValueType: False
record new() IsValueType: False
record IsClass: True
record constructor
Class Before c1 = c2: c1.x = 1, 2.x = 2
Class After c1 = c2 and c1.x = 3: c1.x = 3, c2.x = 3
Structure Before s1 = s2: s1.x = 1, s2.x = 2
Structure After s1 = s2 and s1.x = 3: 1.x = 3, s2.x = 2
Record Before r1 = r2: r1.x = 1, r2.x = 2
Record After r1 = r2 and r1.x = 3: r1.x = 3, r2.x = 3
Equality is value-based
False
False
True
Aircraft { RealName = Boeing, Nickname = Dreamliner, AircraftClassificationNumber = 787 }
Deconstruct method for positional records
Boeing
Dreamliner
787
Record with-expressions
Vehicle { RealName = Airbus, Nickname = Dreamliner }
Vehicle { RealName = Boeing, Nickname = Superjumbo }
Vehicle { RealName = Airbus, Nickname = Superjumbo }
Aircraft { RealName = Airbus, Nickname = Superjumbo, AircraftClassificationNumber = 380 }
Games { Name = Paris 2024 }
Olympics { Name = Los Angeles 2028 }
2. Init only setters
Browser = Chromium, Sessions = 3000, with 3.5 pages/session.
4. Pattern matching enhancements
True
False
True
True
False
is not null
5. Native sized integers
nint.MinValue = -9223372036854775808
nuint.MaxValue = 18446744073709551615
6. Function pointers
Marshal: unsafe delegate can refer non-static method
Function pointer to static method
Function pointer to static method: Hello, World!
Function pointer to static method: 1
Function pointer: delegate managed
Function pointer: Marshal: delegate unmanaged
Function pointer: delegate equivalent
delegate equivalent return: 0
Function pointer: delegate equivalent managed round trip
delegate equivalent managed round trip return: 0
Function pointer: delegate managed equi
delegate managed equi return: 0
Function pointer: delegate equi managed round trip
delegate equi managed round trip return: 0
7. Suppress emitting localsinit flag
SkipLocalsInitAttribute for stackalloc: 0, 1, 2
8. Target-typed new expressions
Scientia
9. static anonymous functions
=> static anonymous functions
=> static action: 25
10. Target-typed conditional expressions
False
11. Covariant return types
12. Extension GetEnumerator support for foreach loops
0 1 2 3 4 5 6 7 8 9
0 1
2 3
4 5
13. Lambda discard parameters
13. Lambda discard parameters (9. static anonymous functions)
14. Attributes on local functions
AllowNull
DisallowNull: warning CS8625, warning CS8603
14. Attributes on local functions
C Library File Name: msys64-mingw64-gcc.so
static extern in local function
C Code: Multiply By 10: By Passing Argument (int.MaxValue / 10) For Parameter (number) To c_code(int number): 2147483640
16. New features for partial methods
partial method out parameter:1
17. Warning wave 5
*/
// Credits:
/*
https://dotnet.microsoft.com/
https://gcc.gnu.org/
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment