.Net Core 6 jumps the shark
-
You were able to install VS Code & dotnet core SDK etc on Zorin and create & compile a C# program on that OS? Very interesting.
Yes, but Zorin OS Lite was apparently not a good choice for bleeding edge things like .Net Core 6.0. Things probably would have been easier on the newer Zorin OS full version using the "Snap package manager". Btw. in this video the new and strange ways of .NET Core 6.0 are explained: Hello World: .NET 6 and .NET Conf - YouTube[^]
-
Kind of. The C# script would be compiled and executed without generating an .EXE on disk. It would all be in-memory. At least, that's the plan. The "script" support for previous C# versions sucks as I recall. It's been a few years since I've dabbled with it.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles.
Dave KreskowiakI use dotnet-script. No real complaints from me. Easy to install
dotnet tool install -g dotnet-script
Easy scaffolding in VS Code (for debug support)dotnet script init
Easy to executedotnet script
You can skip the second step if you just want to execute stuff with no debug support. -
I use dotnet-script. No real complaints from me. Easy to install
dotnet tool install -g dotnet-script
Easy scaffolding in VS Code (for debug support)dotnet script init
Easy to executedotnet script
You can skip the second step if you just want to execute stuff with no debug support.Not usable in my situation. I have to include any libraries in the resulting .EXE and distribute a single executable. Using any "dotnot" commands will not work for me.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles.
Dave Kreskowiak -
Yes, but Zorin OS Lite was apparently not a good choice for bleeding edge things like .Net Core 6.0. Things probably would have been easier on the newer Zorin OS full version using the "Snap package manager". Btw. in this video the new and strange ways of .NET Core 6.0 are explained: Hello World: .NET 6 and .NET Conf - YouTube[^]
-
File this one under Weird. I just built a new project in .NET Core 6 using the command:
c:\> dotnet new console -o SuperConsole
This produced a basic console app that outputs "Hello, World!" Then I took a look at Program.cs and noticed that the entirety of the code is:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");No Main Function!?! Yep, that's right. There is no main function!! Here's the link that explains it. C# template changes in .NET 6 - .NET | Microsoft Docs[^] Here's a bit of explanation from the site:
Explanation at link:
You can look at the code for the new application and imagine that it contains the statements inside the Main method generated by earlier templates. You can add more statements to the program, just like you can add more statements to your Main method in the traditional style. You can even add functions. They're created as local functions nested inside the generated Main method.
I'ma no on that. Functions nested in Main? :| And it tells you that you can still add in the Main method the old way yourself and use it that way. Is C# trying to be JavaScript?
Are you new to C#? this has been in C# since C#7 / .NET.. err.. 4.7? [Local functions - C# Programming Guide | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions)
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
-
File this one under Weird. I just built a new project in .NET Core 6 using the command:
c:\> dotnet new console -o SuperConsole
This produced a basic console app that outputs "Hello, World!" Then I took a look at Program.cs and noticed that the entirety of the code is:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");No Main Function!?! Yep, that's right. There is no main function!! Here's the link that explains it. C# template changes in .NET 6 - .NET | Microsoft Docs[^] Here's a bit of explanation from the site:
Explanation at link:
You can look at the code for the new application and imagine that it contains the statements inside the Main method generated by earlier templates. You can add more statements to the program, just like you can add more statements to your Main method in the traditional style. You can even add functions. They're created as local functions nested inside the generated Main method.
I'ma no on that. Functions nested in Main? :| And it tells you that you can still add in the Main method the old way yourself and use it that way. Is C# trying to be JavaScript?
I think they are going after two main areas: be more like python (REPL approach) and be more like node (see the new asp.net 6 project templates).
Eusebiu
-
Are you new to C#? this has been in C# since C#7 / .NET.. err.. 4.7? [Local functions - C# Programming Guide | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions)
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
Super Lloyd wrote:
.NET.. err.. 4.7?
The version of .NET is irrelevant; it's the compiler and language version that matters. The compiler turns local functions into code that would work in pretty-much any version of .NET - either static functions, instance functions, or functions on a closure class, depending on what you've referenced in the local function. Eg:
void Foo()
{
int Bar() => 42;
Console.WriteLine(Bar());
}becomes something similar to:
[CompilerGenerated]
internal static int <Foo>g__Bar|0_0()
{
return 42;
}void Foo()
{
Console.WriteLine(<Foo>g__Bar|0_0());
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
File this one under Weird. I just built a new project in .NET Core 6 using the command:
c:\> dotnet new console -o SuperConsole
This produced a basic console app that outputs "Hello, World!" Then I took a look at Program.cs and noticed that the entirety of the code is:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");No Main Function!?! Yep, that's right. There is no main function!! Here's the link that explains it. C# template changes in .NET 6 - .NET | Microsoft Docs[^] Here's a bit of explanation from the site:
Explanation at link:
You can look at the code for the new application and imagine that it contains the statements inside the Main method generated by earlier templates. You can add more statements to the program, just like you can add more statements to your Main method in the traditional style. You can even add functions. They're created as local functions nested inside the generated Main method.
I'ma no on that. Functions nested in Main? :| And it tells you that you can still add in the Main method the old way yourself and use it that way. Is C# trying to be JavaScript?
-
File this one under Weird. I just built a new project in .NET Core 6 using the command:
c:\> dotnet new console -o SuperConsole
This produced a basic console app that outputs "Hello, World!" Then I took a look at Program.cs and noticed that the entirety of the code is:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");No Main Function!?! Yep, that's right. There is no main function!! Here's the link that explains it. C# template changes in .NET 6 - .NET | Microsoft Docs[^] Here's a bit of explanation from the site:
Explanation at link:
You can look at the code for the new application and imagine that it contains the statements inside the Main method generated by earlier templates. You can add more statements to the program, just like you can add more statements to your Main method in the traditional style. You can even add functions. They're created as local functions nested inside the generated Main method.
I'ma no on that. Functions nested in Main? :| And it tells you that you can still add in the Main method the old way yourself and use it that way. Is C# trying to be JavaScript?
That's a simple continuation of the "pay for play" philosophy. While the unavailability of the Main function is a thing I really hate about Python (how the hell am I supposed to know where complex code starts operating), it's absence is a huge win for small code bases. Don't get me wrong, for a kLoC of code, spread across 4 or so different modules, the lack of structure which this particular C# template brings to the table would be a bloody nightmare (which is why I'm not using this style for my kLoC-multimodule project). But for something of only mild complexity, that's a win. Boilerplate code, like any other overhead, starts paying off eventually, but if you have something not nearly huge enough for that overhead to pay off, low-overhead alternatives rule. Take file system as an example. NTFS (or ext, if you're so inclined) is by orders of magnitude more advanced, than FAT. Yet, FAT (be it FAT32 or exFAT) got it's own raison d'etre, which is low-requirements-low-overhead. PS: that part that you highlighted, namely local functions, is older, than .NET 6. They started with C# 7.0 which started it's life with .NET 4*x.
-
File this one under Weird. I just built a new project in .NET Core 6 using the command:
c:\> dotnet new console -o SuperConsole
This produced a basic console app that outputs "Hello, World!" Then I took a look at Program.cs and noticed that the entirety of the code is:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");No Main Function!?! Yep, that's right. There is no main function!! Here's the link that explains it. C# template changes in .NET 6 - .NET | Microsoft Docs[^] Here's a bit of explanation from the site:
Explanation at link:
You can look at the code for the new application and imagine that it contains the statements inside the Main method generated by earlier templates. You can add more statements to the program, just like you can add more statements to your Main method in the traditional style. You can even add functions. They're created as local functions nested inside the generated Main method.
I'ma no on that. Functions nested in Main? :| And it tells you that you can still add in the Main method the old way yourself and use it that way. Is C# trying to be JavaScript?
There are a number of shorthand changes (but little innovation) that have been made to C# over the years that are of limited or questionable value. It is a good idea to test out these shorthand C# changes, then look at what the compiler does with them by looking at the generated MSIL. As one example, having done that, it is why I no longer use "using" for IDisposable objects. If you like a particular shortcut, use it. But my advice is to at least know what the compiler does with it. In the case of the OP, just make your own Main() and go with it.
-
Kind of. The C# script would be compiled and executed without generating an .EXE on disk. It would all be in-memory. At least, that's the plan. The "script" support for previous C# versions sucks as I recall. It's been a few years since I've dabbled with it.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles.
Dave KreskowiakI did that a few years ago. It makes a nice scripting utility. I had to figure out what shortcuts in the IDE were not in the compiler, but that did not take long. Mine worked out well, and I used it in a production app. I added in some safeguards, like making sure the C# script had not been tampered with (lots of opportunity for a disgruntled employee to alter an existing script to wreck havoc on production systems).
-
File this one under Weird. I just built a new project in .NET Core 6 using the command:
c:\> dotnet new console -o SuperConsole
This produced a basic console app that outputs "Hello, World!" Then I took a look at Program.cs and noticed that the entirety of the code is:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");No Main Function!?! Yep, that's right. There is no main function!! Here's the link that explains it. C# template changes in .NET 6 - .NET | Microsoft Docs[^] Here's a bit of explanation from the site:
Explanation at link:
You can look at the code for the new application and imagine that it contains the statements inside the Main method generated by earlier templates. You can add more statements to the program, just like you can add more statements to your Main method in the traditional style. You can even add functions. They're created as local functions nested inside the generated Main method.
I'ma no on that. Functions nested in Main? :| And it tells you that you can still add in the Main method the old way yourself and use it that way. Is C# trying to be JavaScript?
-
There are a number of shorthand changes (but little innovation) that have been made to C# over the years that are of limited or questionable value. It is a good idea to test out these shorthand C# changes, then look at what the compiler does with them by looking at the generated MSIL. As one example, having done that, it is why I no longer use "using" for IDisposable objects. If you like a particular shortcut, use it. But my advice is to at least know what the compiler does with it. In the case of the OP, just make your own Main() and go with it.
Could you expand on what you meant by the
using
example? From what I can see, these end up equivalent:using (SomeResource res = new SomeResource())
{
//using block
}
//-------------
SomeResource res = new SomeResource();
try
{
//using block
}
finally
{
if (res != null)
((IDisposable)res).Dispose();
}which seems right to me.
-
Could you expand on what you meant by the
using
example? From what I can see, these end up equivalent:using (SomeResource res = new SomeResource())
{
//using block
}
//-------------
SomeResource res = new SomeResource();
try
{
//using block
}
finally
{
if (res != null)
((IDisposable)res).Dispose();
}which seems right to me.
In short, the using statement swallows constructor errors. Since the actual code being executed is a try … finally, why not just use try … finally (or better yet, try… catch … finally) and use your own code for capturing and logging all exception? And given the unpredictability of the GC, scalability is better served by following the principle, “if you create an object, clean it up when done with it”. Relying on the GC and using shortcuts like the using statement are things I consider poor engineering choices in the context of the SDLC. Others may disagree, but I have yet to see a reasoned argument against my approach that ends in better software.
-
In short, the using statement swallows constructor errors. Since the actual code being executed is a try … finally, why not just use try … finally (or better yet, try… catch … finally) and use your own code for capturing and logging all exception? And given the unpredictability of the GC, scalability is better served by following the principle, “if you create an object, clean it up when done with it”. Relying on the GC and using shortcuts like the using statement are things I consider poor engineering choices in the context of the SDLC. Others may disagree, but I have yet to see a reasoned argument against my approach that ends in better software.
Aah, ok. I can understand not wanting to use the pattern if you need to
catch
exceptions or heavily expect that you will need to in the future with that class (if you're gonna have to expand theusing
into atry...catch
anyways might as well go ahead and do it). That being said, if the exceptions happen in the constructor, they will not be swallowed since object creation happens outside of thetry...finally
. The only exceptions that would be swallowed are the ones that occur while using the object. Tested using:class Test : IDisposable
{
public Test() => throw new NotImplementedException();public void Dispose() {}
}
using (Test t = new Test())
{
Console.WriteLine("constructor exception swallowed.");
} -
Aah, ok. I can understand not wanting to use the pattern if you need to
catch
exceptions or heavily expect that you will need to in the future with that class (if you're gonna have to expand theusing
into atry...catch
anyways might as well go ahead and do it). That being said, if the exceptions happen in the constructor, they will not be swallowed since object creation happens outside of thetry...finally
. The only exceptions that would be swallowed are the ones that occur while using the object. Tested using:class Test : IDisposable
{
public Test() => throw new NotImplementedException();public void Dispose() {}
}
using (Test t = new Test())
{
Console.WriteLine("constructor exception swallowed.");
}I did a demo on this about 1-1/2 years ago, as my fellow developers didn’t see the harm, either. They did afterwards. Since in a well designed app, the try-catch-finally is mostly copy and paste, it really does not save any meaningful development time to use the using statement. Exception handling is a key to reducing dev and QA testing, as well as production troubleshooting. By utilizing the exception’s Data collection, the developer can capture runtime values that are very helpful in diagnosing problems in execution. I have, on many occasions, seen production troubleshooting that would have taken a day or more, shortened to minutes, by smart exception handling. In many production systems, that difference in time can mean thousands to millions in revenue losses avoided by significantly quicker resolution. Using us a shortcut that alleviates the burden of a developer having to remember to call Dispose(). I’d rather use developers who don’t need such shortcuts.
-
I did a demo on this about 1-1/2 years ago, as my fellow developers didn’t see the harm, either. They did afterwards. Since in a well designed app, the try-catch-finally is mostly copy and paste, it really does not save any meaningful development time to use the using statement. Exception handling is a key to reducing dev and QA testing, as well as production troubleshooting. By utilizing the exception’s Data collection, the developer can capture runtime values that are very helpful in diagnosing problems in execution. I have, on many occasions, seen production troubleshooting that would have taken a day or more, shortened to minutes, by smart exception handling. In many production systems, that difference in time can mean thousands to millions in revenue losses avoided by significantly quicker resolution. Using us a shortcut that alleviates the burden of a developer having to remember to call Dispose(). I’d rather use developers who don’t need such shortcuts.
I view it as more of an option to reduce code noise in specific circumstances. I don't disagree with your examples but I don't see the path to the conclusion of inherent harm. Playing devil's advocate on myself, I can see the argument that maybe the class itself won't throw once constructed but something could go terribly wrong with the resource. On the one hand if that's somewhat expected like with a network connection, I can see the merit in saying
using
would be harmful for the reasons you've stated. On the other hand, if errors in the resource are unrecoverable at this layer, then I would argueusing
explicitly states your expectation - this code should not throw and if it does something has gone horribly wrong we can't recover from here so let the exception propogate upwards. In this situation the whole point is thefinally
block, not the exception handling. I agree it can be misused but non-recoveringcatch
es are often misused by improperly re-throwing and losing stack-trace information. I still see the value in re-throwing though if you want to add explicit information to the exception's data to help with debugging. -
I did a demo on this about 1-1/2 years ago, as my fellow developers didn’t see the harm, either. They did afterwards. Since in a well designed app, the try-catch-finally is mostly copy and paste, it really does not save any meaningful development time to use the using statement. Exception handling is a key to reducing dev and QA testing, as well as production troubleshooting. By utilizing the exception’s Data collection, the developer can capture runtime values that are very helpful in diagnosing problems in execution. I have, on many occasions, seen production troubleshooting that would have taken a day or more, shortened to minutes, by smart exception handling. In many production systems, that difference in time can mean thousands to millions in revenue losses avoided by significantly quicker resolution. Using us a shortcut that alleviates the burden of a developer having to remember to call Dispose(). I’d rather use developers who don’t need such shortcuts.
A
using
block does not swallow exceptions, any more than atry..finally
would. The only time an exception would be lost would be if yourDispose
method throws an exception. Whilst that's not entirely unheard of, it's a sign of a poorly implemented class, not a reason to reject theusing
block outright - especially since you'd have exactly the same problem with atry..finally
block. Code written with ausing
block is going to be significantly more "correct" than code that eschews it in favour of manual clean-up. Maybe you should post your demo code to try to convince us.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
A
using
block does not swallow exceptions, any more than atry..finally
would. The only time an exception would be lost would be if yourDispose
method throws an exception. Whilst that's not entirely unheard of, it's a sign of a poorly implemented class, not a reason to reject theusing
block outright - especially since you'd have exactly the same problem with atry..finally
block. Code written with ausing
block is going to be significantly more "correct" than code that eschews it in favour of manual clean-up. Maybe you should post your demo code to try to convince us.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Quote:
Code written with a using block is going to be significantly more "correct" than code that eschews it in favour of manual clean-up.
That comes across as more religion than a rational approach. In order to illustrate my point, I can add the C# code I used for the test (in .NET 6.0) and the MSIL output of both. The unit test for the "using" statement took 28 ms, while the standard use took 18 ms. The MSIL for the "using" statement produced 47 lines of MSIL code, while the standard approach without "using" produced 31 lines of MSIL code. I kept the IDisposable instance simple for this example. In order to capture the constructor exception, the using block has to be wrapped with a try-catch. The results of this test, combined with how I capture exception data and the runtime values associated with the exception (to significantly reduce support costs of the SDLC), is why I do not use the "using" statement in production apps. I do use it where appropriate in proof-of-concept and personal utilities, where support and performance is less of a concern. IDisposable class:
namespace UsingTest
{
public class DisposableClass : IDisposable
{private Boolean m\_blnDisposeHasBeenCalled = false; public DisposableClass() { Int32 denom = 20; Int32 numer = 0; Int32 result = denom / numer; } public String WhoIsIt() { return $"{Environment.UserDomainName}\\\\{Environment.UserName} on {Environment.MachineName} from {Environment.CurrentDirectory} on thread ID {Environment.CurrentManagedThreadId.ToString()}."; } #region IDisposable Implementation /// /// Implement the IDisposable.Dispose() method /// Developers are supposed to call this method when done with this Object. /// There is no guarantee when or if the GC will call it, so /// the developer is responsible to. GC does NOT clean up unmanaged /// resources, such as COM objects, so we have to clean those up, too. /// /// public void Dispose() { try { // Check if Dispose has already been called // Only allow the consumer to call it once with effect. if (!m\_blnDisposeHasBeenCalled) { // Call the overridden Dispose method that contains common cleanup code // Pass true to indicate that it is called from Dispose Dispose(true); // Prevent subsequent finalization of this Object. This is not needed // because managed and unmanaged res
-
Quote:
Code written with a using block is going to be significantly more "correct" than code that eschews it in favour of manual clean-up.
That comes across as more religion than a rational approach. In order to illustrate my point, I can add the C# code I used for the test (in .NET 6.0) and the MSIL output of both. The unit test for the "using" statement took 28 ms, while the standard use took 18 ms. The MSIL for the "using" statement produced 47 lines of MSIL code, while the standard approach without "using" produced 31 lines of MSIL code. I kept the IDisposable instance simple for this example. In order to capture the constructor exception, the using block has to be wrapped with a try-catch. The results of this test, combined with how I capture exception data and the runtime values associated with the exception (to significantly reduce support costs of the SDLC), is why I do not use the "using" statement in production apps. I do use it where appropriate in proof-of-concept and personal utilities, where support and performance is less of a concern. IDisposable class:
namespace UsingTest
{
public class DisposableClass : IDisposable
{private Boolean m\_blnDisposeHasBeenCalled = false; public DisposableClass() { Int32 denom = 20; Int32 numer = 0; Int32 result = denom / numer; } public String WhoIsIt() { return $"{Environment.UserDomainName}\\\\{Environment.UserName} on {Environment.MachineName} from {Environment.CurrentDirectory} on thread ID {Environment.CurrentManagedThreadId.ToString()}."; } #region IDisposable Implementation /// /// Implement the IDisposable.Dispose() method /// Developers are supposed to call this method when done with this Object. /// There is no guarantee when or if the GC will call it, so /// the developer is responsible to. GC does NOT clean up unmanaged /// resources, such as COM objects, so we have to clean those up, too. /// /// public void Dispose() { try { // Check if Dispose has already been called // Only allow the consumer to call it once with effect. if (!m\_blnDisposeHasBeenCalled) { // Call the overridden Dispose method that contains common cleanup code // Pass true to indicate that it is called from Dispose Dispose(true); // Prevent subsequent finalization of this Object. This is not needed // because managed and unmanaged res
MSBassSinger wrote:
That comes across as more religion than a rational approach.
Says the person trying to convince me that a
using
block swallows exceptions, whilst showing no evidence of it doing so. :doh:MSBassSinger wrote:
The unit test for the "using" statement took 28 ms, while the standard use took 18 ms.
So code with different behaviour, compiled in a debug build, has approximately 10ms difference in your tests? Aside from the issue of micro-optimisation - any extra overhead from the
using
construct will be dwarfed by the cost of your real code - a unit test is not suitable for micro-benchmarks. You need to "warm up" your code, then measure performance over thousands of runs to get a meaningful result. Try using BenchmarkDotNet[^] to measure the code instead. And while you're at it, fix your tests so that you're comparing the same - or at least comparable - code. Change your "standard" test to:public void StandardUsageTest()
{
try
{
DisposableClass test = null;
try
{
test = new();
try
{
String result = test.WhoIsIt();
}
catch (Exception ex)
{
Assert.Fail($"INNER: {ex.Message}");
}
}
finally
{
test?.Dispose();
}
}
catch (Exception exOuter)
{
Assert.Fail($"OUTER: {exOuter.Message}");
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer