TheSwamp

Code Red => .NET => Topic started by: kdub_nz on July 07, 2023, 11:27:52 PM

Title: Playing with C#8 in Framework 4.8
Post by: kdub_nz on July 07, 2023, 11:27:52 PM
It's a rainy Saturday, so . . .

modified the .csproj file
Code - XML: [Select]
  1.   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
  2.     <DebugSymbols>true</DebugSymbols>
  3.     <OutputPath>bin\x64\Debug\</OutputPath>
  4.     <DefineConstants>DEBUG;TRACE</DefineConstants>
  5.     <DebugType>full</DebugType>
  6.     <PlatformTarget>x64</PlatformTarget>
  7.        
  8.         <!-- <LangVersion>7.3</LangVersion> -->
  9.     <LangVersion>8.0</LangVersion>
  10.     <Nullable>enable</Nullable>
  11.        
  12.     <ErrorReport>prompt</ErrorReport>
  13.   </PropertyGroup>
  14.   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
  15.     <OutputPath>bin\x64\Release\</OutputPath>
  16.     <DefineConstants>TRACE</DefineConstants>
  17.     <Optimize>true</Optimize>
  18.     <DebugType>pdbonly</DebugType>
  19.     <PlatformTarget>x64</PlatformTarget>
  20.        
  21.         <!-- <LangVersion>7.3</LangVersion> -->
  22.     <LangVersion>8.0</LangVersion>
  23.     <Nullable>enable</Nullable>
  24.        
  25.     <ErrorReport>prompt</ErrorReport>
  26.   </PropertyGroup>
  27.  


and had a play . . .

Code - C#: [Select]
  1. public class CommandsTest
  2.    {
  3.       private static readonly Document _doc = CadApp.DocumentManager.MdiActiveDocument;
  4.       private static readonly Database _db = _doc.Database;
  5.       private static readonly Editor _ed = _doc.Editor;
  6.  
  7.       //----Test C# 8 in Framework -----------------------------------------------
  8.       public string? NullableString { get; set; }
  9.       private string _name;
  10.       public string Name
  11.       {
  12.          get => _name;
  13.          set => _name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
  14.       }
  15.  
  16.       [CommandMethod("Test_cs8")]
  17.       public void Test_cs8()
  18.       {
  19.  
  20.          NullableString = null;
  21.  
  22.          _ed.WriteMessage($"NullableString : {NullableString}\n");
  23.          // If null, give it a value
  24.          NullableString ??= "Phred Qnurks";
  25.          _ed.WriteMessage($"NullableString : {NullableString}\n");
  26.  
  27.          NullableString ??= "Humpty Dumpty";  // this assignment should be ignored
  28.                                               // 'cause NullableString has a value
  29.          _ed.WriteMessage($"NullableString : {NullableString}\n");
  30.          //-------------
  31.  
  32.          List<int> numbers = null;
  33.          int? a = null;
  34.  
  35.          _ed.WriteMessage($"(numbers is null) : {(numbers is null)}\n");
  36.          // expected: true
  37.  
  38.          // if numbers is null, initialize it. Then, add 5 to numbers
  39.          (numbers ??= new List<int>()).Add(5);
  40.          _ed.WriteMessage(string.Join(" ", numbers) + "\n");
  41.          // output: 5
  42.  
  43.          _ed.WriteMessage($"{(numbers is null)} \n");
  44.          // expected: false
  45.  
  46.          _ed.WriteMessage($"(a is null) : {(a is null)} \n");
  47.          // expected: true
  48.  
  49.          _ed.WriteMessage($"(a ?? 3) : {(a ?? 3)}\n");
  50.          // expected: 3 since a is still null
  51.  
  52.  
  53.          int? b = null;
  54.  
  55.          int c = b ?? -1;
  56.  
  57.          _ed.WriteMessage($"value c : {c}\n");
  58.  
  59.  
  60.          Name = "Phred";
  61.          _ed.WriteMessage($"value Name : {Name}\n");
  62.  
  63.       }
  64.    }
  65.  

then opened the beast . . .


Command: netload Assembly file name: "Teststuff.dll"
Command:
Teststuff loaded.  : Right-Click for Menu.
Command: TEST_CS8
NullableString :
NullableString : Phred Qnurks
NullableString : Phred Qnurks
(numbers is null) : True
5
False
(a is null) : True
(a ?? 3) : 3
value c : -1
value Name : Phred



ref :
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
Title: Re: Playing with C#8 in Framework 4.8
Post by: It's Alive! on July 07, 2023, 11:35:04 PM
Like a whole new language!  Spaceship operator?
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on July 07, 2023, 11:50:21 PM

the null conditional operator '?'  in C# (also known as null propagation operator or the Elvis operator).

The three-way comparison operator '<=>' is called a spaceship operator from C++

?? and ??= operators - the null-coalescing operators in C# - not sure if they have geek alias names
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on July 07, 2023, 11:58:25 PM

I'm working through the C# 8 updates
Then C# 9  ; .net 5
Then C# 10 ; .net 6
Then C# 11 ; .net 7

I had a play with .NET 8 preview in the VS2022 17.7.0 preview 2.0 last night.
the new net build won't be released till November.
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on July 08, 2023, 06:14:29 PM
Because of AutoDesk's previous decision to only support the .net Framework I have about 5 years of C# versions to catch up on. Rightly or not, I had taken the attitude "we can't use it, so I won't attempt to grok it" I hope that attempt to keep some spare brain capacity doesn't bite me in the arse.

Regarding the use of Nullable reference types :

https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references
https://learn.microsoft.com/en-us/dotnet/csharp/nullable-migration-strategies

Be aware about blindly adding  <Nullable>enable</Nullable> to the .csprog project file
"The new features that protect against throwing a System.NullReferenceException can be disruptive when turned on in an existing codebase:

At least the compiler will throw warnings.

I think I'll take a couple weeks to re-read all this, it's too important to stuff-up.

Starting in .NET 6, ( C#10 ) they're enabled by default for new projects.

The first decision to make is 'do I want to use Nullable reference types'

Quote from: https://www.dotnetcurry.com/csharp/nullable-reference-types-csharp
Because of the language history, the decision to use the same syntax for value types and reference types changes the behavior of the language for reference types. Before C# 8, all reference types were nullable. They were however declared without a question mark:

Title: Re: Playing with C#8 in Framework 4.8
Post by: It's Alive! on July 09, 2023, 02:00:10 AM
I’m reading it as a good thing. The compiler will force you to check for null, right?
C++ is getting better at warning about null, though the static analysis isn’t perfect.  Example, if I use a getter, the compiler sometimes whines 

Code - C++: [Select]
  1. wxWindow* PyCAdUiPaletteImpl::thiswindow(const std::source_location& src /*= std::source_location::current()*/) const
  2. {
  3.     if (m_thiswin == nullptr)
  4.         throw PyNullObject(src);
  5.     return m_thiswin;
  6. }
  7.  


 
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on July 09, 2023, 03:14:41 AM
I’m reading it as a good thing. The compiler will force you to check for null, right?
C++ is getting better at warning about null, though the static analysis isn’t perfect.  Example, if I use a getter, the compiler sometimes whines 
>>> . . .

Yes, that's the way I see it too Daniel.

I added the 'enable' flag to one of my test projects.
Scared me a little to see the warnings that the compiler threw up regarding 'possible null' values.
. . . but easily fixed I think

This throws on acLayout
Code - C#: [Select]
  1. Layout acLayout = tr.GetObject(objID, OpenMode.ForRead) as Layout;
and this on appContextMnuItem
Code - C#: [Select]
  1. AppContextMenuItem appContextMnuItem = mnuItem as AppContextMenuItem;
Warning   CS8602   Dereference of a possibly null reference.   

While these are ok:
Code - C#: [Select]
  1. Layout acLayout = (Layout)tr.GetObject(objID, OpenMode.ForRead);
Code - C#: [Select]
  1. AppContextMenuItem appContextMnuItem = (AppContextMenuItem)mnuItem ;

So it looks like we'll make sure of casting.


another:
Code - C#: [Select]
  1. PromptPointResult pointResult = default;
for pointResult
Warning   CS8600   Converting null literal or possible null value to non-nullable type.   

Title: Re: Playing with C#8 in Framework 4.8
Post by: It's Alive! on July 09, 2023, 04:05:10 AM
 I don’t know the consequences of using a C style cast. The ‘as’ with a null check seems better
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on July 09, 2023, 06:15:57 AM
I don’t know the consequences of using a C style cast. The ‘as’ with a null check seems better

yes,
On failure, the as (soft cast) returns null,
and the (hard) cast throws an exception,
so the issue has to be handled either way.

A guard clause on the 'as' might be cleaner and faster.  Testing will tell.

I'd bet a couple pennys 'most' people would just ignore the compiler warnings saying "I've never had a problem before, so . . . "


Title: Re: Playing with C#8 in Framework 4.8
Post by: BlackBox on July 11, 2023, 02:00:17 PM
I don’t know the consequences of using a C style cast. The ‘as’ with a null check seems better

yes,
On failure, the as (soft cast) returns null,
and the (hard) cast throws an exception,
so the issue has to be handled either way.

A guard clause on the 'as' might be cleaner and faster.  Testing will tell.

I'd bet a couple pennys 'most' people would just ignore the compiler warnings saying "I've never had a problem before, so . . . "

I usually direct cast in lieu of using 'as' where my code is based on selection sets; the Type is already accounted for and a cast-specific handling for Null or Exception isn't typically needed (in addition to the method's try catch block).
Title: Re: Playing with C#8 in Framework 4.8
Post by: gile on August 07, 2023, 01:36:21 PM
Hi,

Thanks Kerry.
Playing with pattern matching.

Example without pattern matching (C# 6):
Code - C#: [Select]
  1.         private static void SetColor(Entity entity)
  2.         {
  3.             int colorIndex = 256;
  4.             if (entity is BlockReference)
  5.             {
  6.                 var b = (BlockReference)entity;
  7.                 if (b.Position.Z == 0.0)
  8.                 {
  9.                     colorIndex = 31;
  10.                 }
  11.             }
  12.             else if (entity is Dimension)
  13.             {
  14.                 var d = (Dimension)entity;
  15.                 if (d.DimensionText != "")
  16.                 {
  17.                     colorIndex = 141;
  18.                 }
  19.             }
  20.             else if (entity is DBPoint)
  21.             {
  22.                 colorIndex = 241;
  23.             }
  24.             entity.ColorIndex = colorIndex;
  25.         }

Witch C# 7 pattern matching:
Code - C#: [Select]
  1.        private static void SetColor(Entity entity)
  2.         {
  3.             int colorIndex;
  4.             switch (entity)
  5.             {
  6.                 case BlockReference b when b.Position.Z != 0.0:
  7.                     colorIndex = 31;
  8.                     break;
  9.                 case Dimension d when d.DimensionText != "":
  10.                     colorIndex = 141;
  11.                     break;
  12.                 case DBPoint _:
  13.                     colorIndex = 241;
  14.                     break;
  15.                 default:
  16.                     colorIndex = 256;
  17.                     break;
  18.             }
  19.             entity.ColorIndex = colorIndex;
  20.         }

With C# 8 pattern matching:
Code - C#: [Select]
  1.         private static void SetColor(Entity entity) =>
  2.             entity.ColorIndex = entity switch
  3.             {
  4.                 BlockReference b when b.Position.Z != 0.0 => 31,
  5.                 Dimension d when d.DimensionText != "" => 141,
  6.                 DBPoint _ => 241,
  7.                 _ => 256
  8.             };

With F# 1.0:
Code - F#: [Select]
  1. let setColor (entity: Entity) =
  2.     entity.ColorIndex <-
  3.         match entity with
  4.         | :? BlockReference as b when b.Position.Z <> 0.0 -> 31
  5.         | :? Dimension as d when d.DimensionText <> "" -> 141
  6.         | :? DBPoint -> 241
  7.         | _ -> 256

Life imitates art.
C# imitates F#.
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on August 07, 2023, 06:13:04 PM
Hi Gilles,
Yes, It's very nice.
Unfortunately my old brain stutters with F#.

With any 'luck' we'll all be writing code like your With C# 8 pattern matching: by this time next year   :wink:

I've signed up for the Alpha/Beta testing for AC2025 so I can't say much more about the planned specifics, but I have my Windows Sandbox ready for testing when the revevant Alpha/Beta arrives.

Meanwhile, all we can do is to become familiar with the new .NET builds ( as we've been doing here )

added: Those are excellent examples of the functionality . . . well worth the study for anyone.
Thanks,
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on August 07, 2023, 10:38:41 PM
For interests sake, this is the de-compiled code of the  SetColor Method using C# 8 pattern matching:,
just to show how VS handles the pretty switch statement :)

Please ignore the variable names used, they are assigned by my Reflector according to the Type of the value

I think that this is a perfect example of how making significant savings in code time may not reduce the actual compiled code.
. . . but the original source sure IS pretty to read.

Code - C#: [Select]
  1. [NullableContext(1)]
  2. private static void SetColor(Entity entity)
  3. {
  4.     int num;
  5.     Entity entity2 = entity;
  6.     Entity entity3 = entity;
  7.     if (1 == 0)
  8.     {
  9.     }
  10.     BlockReference reference2 = entity3 as BlockReference;
  11.     if (reference2 != null)
  12.     {
  13.         if (reference2.Position.Z != 0.0)
  14.         {
  15.             num = 0x1f;
  16.             goto TR_0001;
  17.         }
  18.     }
  19.     else
  20.     {
  21.         Dimension dimension2 = entity3 as Dimension;
  22.         if (dimension2 != null)
  23.         {
  24.             if (dimension2.DimensionText != "")
  25.             {
  26.                 num = 0x8d;
  27.                 goto TR_0001;
  28.             }
  29.         }
  30.         else if (entity3 is DBPoint)
  31.         {
  32.             num = 0xf1;
  33.             goto TR_0001;
  34.         }
  35.         else if (entity3 == null)
  36.         {
  37.             if (1 == 0)
  38.             {
  39.             }
  40.             <PrivateImplementationDetails>.ThrowInvalidOperationException();
  41.             goto TR_0001;
  42.         }
  43.     }
  44.     num = 0x100;
  45. TR_0001:
  46.     if (1 == 0)
  47.     {
  48.     }
  49.     entity2.ColorIndex = num;
  50. }
  51.  
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on August 07, 2023, 11:29:29 PM
For anyone who wants to play, this is one way to call it :

Code - C#: [Select]
  1.      [CommandMethod("SetColor")]
  2.      public static void SetColor()
  3.      {
  4.         var doc = CadApp.DocumentManager.MdiActiveDocument;        
  5.  
  6.         var result = doc.Editor.GetEntity("Select Entity");
  7.         if (result.Status != PromptStatus.OK)
  8.            return;  
  9.  
  10.         using (var tr = new OpenCloseTransaction())
  11.         {
  12.            var ent = (Entity)tr.GetObject(result.ObjectId,
  13.               OpenMode.ForWrite, false, false);
  14.            SetColor( ent );
  15.  
  16.            tr.Commit();
  17.         }
  18.      }
  19.  
  20.  
Title: Re: Playing with C#8 in Framework 4.8
Post by: 57gmc on August 08, 2023, 01:53:03 PM

I'm working through the C# 8 updates
Then C# 9  ; .net 5
Then C# 10 ; .net 6
Then C# 11 ; .net 7

I had a play with .NET 8 preview in the VS2022 17.7.0 preview 2.0 last night.
the new net build won't be released till November.
I was searching on this subject and found this interesting table (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version#defaults).
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on August 09, 2023, 11:35:31 PM
FWIW :
C# 11 is as far as I've gone.

No Blow-Ups yet . . . but my testing has been pretty simple, so far.
Perhaps take care not to use on production code !!

Using Microsoft Visual Studio Community 2022 (64-bit) - Preview
Version 17.8.0 Preview 1.0

My current .csproj file
The references to NUnit will be removed tonight

Code - XML: [Select]
  1. <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  2.   <Import Project="..\packages\NUnit.3.13.3\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.13.3\build\NUnit.props')" />
  3.   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  4.   <PropertyGroup>
  5.     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  6.     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
  7.     <ProjectGuid>{BCA3F716-BC8B-4DB0-B38E-6DB783DD0576}</ProjectGuid>
  8.     <OutputType>Library</OutputType>
  9.     <AppDesignerFolder>Properties</AppDesignerFolder>
  10.     <RootNamespace>TestStuff_230711</RootNamespace>
  11.     <AssemblyName>TestStuff_230711</AssemblyName>
  12.     <TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
  13.     <FileAlignment>512</FileAlignment>
  14.     <Deterministic>true</Deterministic>
  15.     <TargetFrameworkProfile />
  16.     <NuGetPackageImportStamp>
  17.     </NuGetPackageImportStamp>
  18.   </PropertyGroup>
  19.   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  20.     <DebugSymbols>true</DebugSymbols>
  21.     <DebugType>full</DebugType>
  22.     <Optimize>false</Optimize>
  23.     <DefineConstants>DEBUG;TRACE</DefineConstants>
  24.     <ErrorReport>prompt</ErrorReport>
  25.     <WarningLevel>4</WarningLevel>
  26.     <StartAction>Program</StartAction>
  27.     <StartProgram>C:\Program Files\Autodesk\AutoCAD 2023\acad.exe</StartProgram>
  28.     <StartArguments>/nologo /b "start.scr"</StartArguments>
  29.  
  30. <!-- revised kdub -->
  31.     <OutputPath>bin\x64\Debug\</OutputPath>
  32.     <PlatformTarget>x64</PlatformTarget>
  33.     <!-- <LangVersion>7.3</LangVersion> -->
  34.     <!-- <LangVersion>8.0</LangVersion> -->
  35.     <LangVersion>11.0</LangVersion>
  36.     <Nullable>enable</Nullable>
  37. <!-- revised kdub -->
  38.  
  39.     <DocumentationFile>bin\x64\Debug\TestStuff_230711.xml</DocumentationFile>
  40.   </PropertyGroup>
  41.   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  42.     <DebugType>pdbonly</DebugType>
  43.     <Optimize>true</Optimize>
  44.     <DefineConstants>TRACE</DefineConstants>
  45.     <ErrorReport>prompt</ErrorReport>
  46.     <WarningLevel>4</WarningLevel>
  47.  
  48. <!-- revised kdub -->
  49.     <OutputPath>bin\x64\Release\</OutputPath>
  50.     <PlatformTarget>x64</PlatformTarget>
  51.     <!-- <LangVersion>7.3</LangVersion> -->
  52.     <!-- <LangVersion>8.0</LangVersion> -->
  53.     <LangVersion>11.0</LangVersion>
  54.     <Nullable>enable</Nullable>
  55. <!-- revised kdub -->
  56.  
  57.   </PropertyGroup>
  58.   <ItemGroup>
  59.     <Reference Include="AcCoreMgd">
  60.       <HintPath>..\..\..\..\..\Work\ObjectARX_2023\inc\AcCoreMgd.dll</HintPath>
  61.       <Private>False</Private>
  62.     </Reference>
  63.     <Reference Include="AcDbMgd">
  64.       <HintPath>..\..\..\..\..\Work\ObjectARX_2023\inc\AcDbMgd.dll</HintPath>
  65.       <Private>False</Private>
  66.     </Reference>
  67.     <Reference Include="acdbmgdbrep">
  68.       <HintPath>..\..\..\..\..\Work\ObjectARX_2023\inc\acdbmgdbrep.dll</HintPath>
  69.       <Private>False</Private>
  70.     </Reference>
  71.     <Reference Include="AcMgd">
  72.       <HintPath>..\..\..\..\..\Work\ObjectARX_2023\inc\AcMgd.dll</HintPath>
  73.       <Private>False</Private>
  74.     </Reference>
  75.     <Reference Include="nunit.framework, Version=3.13.3.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
  76.       <HintPath>..\packages\NUnit.3.13.3\lib\net45\nunit.framework.dll</HintPath>
  77.     </Reference>
  78.     <Reference Include="System" />
  79.     <Reference Include="System.Core" />
  80.     <Reference Include="System.Xml.Linq" />
  81.     <Reference Include="System.Data.DataSetExtensions" />
  82.     <Reference Include="Microsoft.CSharp" />
  83.     <Reference Include="System.Data" />
  84.     <Reference Include="System.Net.Http" />
  85.     <Reference Include="System.Xml" />
  86.   </ItemGroup>
  87.   <ItemGroup>
  88.     <Compile Include="Initialization.cs" />
  89.     <Compile Include="PathNetCore.cs" />
  90.     <Compile Include="Test_cs08_11.cs" />
  91.     <Compile Include="Properties\AssemblyInfo.cs" />
  92.   </ItemGroup>
  93.   <ItemGroup>
  94.     <None Include="packages.config" />
  95.     <None Include="start.scr">
  96.       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  97.     </None>
  98.   </ItemGroup>
  99.   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  100.   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
  101.     <PropertyGroup>
  102.       <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
  103.     </PropertyGroup>
  104.     <Error Condition="!Exists('..\packages\NUnit.3.13.3\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.13.3\build\NUnit.props'))" />
  105.   </Target>
  106. </Project>
  107.  
  108.  
Title: Re: Playing with C#8 in Framework 4.8
Post by: kdub_nz on August 10, 2023, 07:43:33 PM
Testing concepts in LinqPad for language changes.

Beginning with C# 11, the interpolated expressions can include newlines.
The text between the { and } must be valid C#, therefore it can include newlines that improve readability.

example of a switch statement in an Interpolated string . . eminently readable !

Of course, the return value of the switch statement could be passed to a layer-changing statement.