C# 7 introduced local functions (which is great!). Suppose I have the following code:
using System;
using PostSharp.Aspects;
namespace AspectCS7
{
class Program
{
private static void Main()
{
[MyAspect]
void LocalFunction()
{
Console.WriteLine("Hello Aspect!");
}
LocalFunction();
}
}
[Serializable]
public class MyAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine("Entering Aspect");
}
}
}
This code shows compile-time errors. Is it possible to apply attributes to local functions?
Attributes were allowed on local functions at one point. There are some examples on the web of local functions using attributes, however they're not allowed anymore.
Update:
Here is an ongoing discussion on this topic: https://github.com/dotnet/csharplang/issues/794.
Related
I recently updated Visual Studio and found out about this new feature (to me it is new) of top-level statements.
As I understand it, the compiler completes the definitions for the Program class and Main method, without you having to explicitly type it up.
This is useful, but I'm having trouble when defining a new method. I would like a method in the Program class. And call this with a top-level statement. Here is some example code:
Console.WriteLine("toplevel");
ThisShouldBeAMethodOfProgramClass();
public static void ThisShouldBeAMethodOfProgramClass()
{
Console.WriteLine("Static in Program class");
}
This is giving me build errors, because the public static modifiers are not valid. I think it interprets this as a local function in Main. I can remove the modifiers, but this is just example code, my real code has more methods and classes.
How can I do this? Should I not use top-level for this?
I would like this to effectively be the same as:
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("toplevel");
ThisShouldBeAMethodOfProgramClass();
}
public static void ThisShouldBeAMethodOfProgramClass()
{
Console.WriteLine("Static in Program class");
}
}
You can keep using top-level statements and append additional members with a partial Program class.
using System;
Console.WriteLine("toplevel");
ThisShouldBeAMethodOfProgramClass();
public static partial class Program
{
public static void ThisShouldBeAMethodOfProgramClass()
{
Console.WriteLine("Static in Program class");
}
}
Or just remove the access modifier: method without access modifier
using System;
Console.WriteLine("toplevel");
ThisShouldBeAMethodOfProgramClass();
static void ThisShouldBeAMethodOfProgramClass()
{
Console.WriteLine("Static in Program class");
}
Consider the following code:
using System;
static class Program
{
public static void Main()
{
Test test = new Test();
test.Method(()=>{});
}
}
public sealed class Test
{
public void Method(Delegate _)
{
Console.WriteLine("Test.Method(Delegate)");
}
}
public static class TestExt
{
public static void Method(this Test self, Action _)
{
Console.WriteLine("TestExt.Method(Action)");
}
}
In C# 9.0 targeting net48, this outputs TestExt.Method(Action)
In C# 10.0 targeting net6.0, this outputs Test.Method(Delegate)
Obviously this is a breaking change because the extension method may execute different code.
My question is: Is there a way to detect this sort of breaking change in the code without inspecting all the code? For example, is there a code analysis rule that we can enable to detect this sort of code?
This change has actually caused a genuine bug in our code base.
Note that this difference in behaviour is only occurring because of changes to the extension method resolution. If instead of an extension method, the method is in the Test class itself then both language versions call Test.Method(Action):
static class Program
{
public static void Main()
{
Test test = new Test();
test.Method(()=>{});
}
}
public sealed class Test
{
public void Method(Delegate _)
{
Console.WriteLine("Test.Method(Delegate)"); // Not called by either version.
}
public void Method(Action _)
{
Console.WriteLine("TestExt.Method(Action)"); // Called by C#9 and C#10.
}
}
Background information:
This breaking change has bitten us because we have (rightly or wrongly) an extension method for Control.BeginInvoke() along these lines (error handling omitted):
public static void BeginInvoke(this Control control, Action action)
{
if (control.IsHandleCreated)
tryBeginInvoke(control, action);
}
This is no longer called for code like:
this.BeginInvoke(() => someMenu.Visible = somethingIsAvailable());
Instead, Control.BeginInvoke(Delegate) is called, which omits the check for control.IsHandleCreated and the tryBeginInvoke() error handling method.
I read the book "CLR via C#" by Jeffrey Richter. In chapter 20, there is a code example demonstrating usage of Constrained Execution Regions (CERs):
private static void Demo2() {
// Force the code in the finally to be eagerly prepared
RuntimeHelpers.PrepareConstrainedRegions(); // System.Runtime.CompilerServices namespace
try {
Console.WriteLine("In try");
}
finally {
// Type2’s static constructor is implicitly called in here
Type2.M();
}
}
public class Type2 {
static Type2() {
Console.WriteLine("Type2's static ctor called");
}
// Use this attribute defined in the System.Runtime.ConstrainedExecution namespace
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void M() { }
}
And the following text:
Now, when I run this version of the code, I get the following output.
Type2's static ctor called
In try
But when I run this code I get the following output no matter if I use CERs or not:
In try
Type2's static ctor called
So, Type2 constructor is getting called after try block (breaking the meaning of CERs usages, as I understand it).
What can be possible reason of this?
EDIT:
I'm using .Net Core 3.1 and VS Enterprise 2019 Preview
Version 16.6.0 Preview 3.0, my code is:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
public sealed class Program
{
public static void Main()
{
Demo2();
}
private static void Demo2()
{
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Console.WriteLine("In try");
}
finally
{
Type2.M();
}
}
}
public class Type2
{
static Type2()
{
Console.WriteLine("Type2's static ctor called");
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void M() { }
}
By the way, Type2 class can be inside the Program, it does not change the output.
(This is not the full answer to what is actually going on here, but it might help you)
Your problem is most likely that you run your process from within Visual Studio (using CTRL+F5). It is then launched via VsDebugConsole.exe, which then starts your process (also see this).
For whatever reason - that would need to be researched - it looks as if CERs are not honored when running in this way.
What you could do to validate, is attempt to run your program directly from the command prompt / console.
Here is a quote from Microsoft's article about Constrained Execution Regions:
CER is only supported in .NET Framework. This article doesn't apply to
.NET Core or .NET 5 and above.
I have a class:
namespace FooIOS
{
public class Foo
{
public static void doThis() {...}
}
}
And this works:
using FooIOS;
namespace Sample.iOS
{
public void method () {
Foo.doThis();
}
}
However, this does not work the same way when I change the namespace to insert a period:
namespace Foo.iOS
{
public class Foo
{
public static void doThis() {...}
}
}
using Foo.iOS;
namespace Sample.iOS
{
public void method () {
// Compilation error
Foo.doThis();
// Compilation error
Foo.iOS.doThis()
// This works but I can't have it that long and complicated (I'm writing an API call)
Foo.iOS.Foo.doThis();
}
}
I'm pretty inexperienced with C# and I'm wondering if there's any way to use the period in the namespace and not deal with the complicated call.
namespace Foo.iOS
{
public class Foo
{
public static void doThis() {...}
}
}
Your namespace name is Foo.iOS, class name is Foo, static method name is doThis(). The fully qualified path to access that method is NAMESPACE.CLASS.METHOD_NAME, so it becomes:
Foo.iOS.Foo.doThis();
Here is nothing wrong with C#, but with the naming you use.
From this a couple of suggestions:
try to no use . inside names of the namespace, as this introduces confusion
try to not name namespace as the class inside it, as this introduces confusion.
I'm pretty inexperienced with C# and I'm wondering if there's any way
to use the period in the namespace and not deal with the complicated
call.
Short answer is: name your namespaces, classes and member functions in a way, that it does not look complicated to you and to others.
EDIT
Consider that you can use also Namespace Alias.
For example:
using IOS = Foo.iOS;
...
IOS.Foo.doThis();
But as I said before, it's better to avoid . in namespace name at first place.
Bring the using Foo.iOS; statement inside the namespace Sample.iOS namespace block, like shown below, then you will be able to call doThis() like in your 1st attempt Foo.doThis(); that was previously giving you a compile error.
namespace Sample.iOS
{
using Foo.iOS;
public void method () {
// this works
Foo.doThis();
}
}
Related reading: Inside or Outside? by Eric Lippert on MSDN.
Fully working Code sample:
Create a new Console App in Visual Studio, and then in the Program.cs class, delete all lines, paste the following, do a compile and then run.
using System;
namespace Foo.iOS
{
public class Foo
{
public static void doThis() { Console.Write("Inside doThis");}
}
}
namespace Sample.iOS
{
using Foo.iOS;
class Program
{
static void Main(string[] args)
{
method();
Console.ReadKey();
}
public static void method ()
{
// works fine
Foo.doThis();
}
}
}
To avoid repetitive definitions, I am trying to move internal function prototypes from the class One into a C# using My.Class.One directive (like import My.Class.One in Java).
In Java, this would change this code:
public class One {
public static void func1();
public static void func2();
public static int main(String[] args) { ... }
}
...into that code split into different files:
import My.Include;
public class One {
public static int Main(String[] args) { ... }
}
------------------------->8--------------------------------
package Include;
public class My {
public static void func1();
public static void func2();
...
}
After learning that Java 'packages' are named 'namespaces' in C# I came up with the following C# code which fails to compile (mcs One.cs My_include.cs -out:One.exe) with the error:
> "error CS0103: The name `func1' does not exist in the current context"
using System;
using My.Include;
public class One {
public static int Main(String[] args) { return funct1(); }
}
------------------------->8--------------------------------
using System;
namespace My {
namespace Include {
public class functions {
public static void func1();
public static void func2();
...
}
}
}
I tried many different naming conventions but I still get the same error. Can you tell me what I do wrong?
It looks like you want a partial class, so your main class file would look like:
// File: One.cs
using System;
public class One
{
static void Main()
{
// Your Main method here
}
}
And you would have a second file which looks like:
// File: One.Externs.cs
using System;
public partial class One
{
extern static void func1();
extern static void func2();
}
At compile-time, these two (or more) files are automatically combined to a single class by the compiler. This gives you the separation you appear to be looking for, but keeps the relevant definitions within the correct scopes.
The using directive is only partially synonymous to java's import directive - in C# using only provides a shortcut into a namespace whereas you're used to referring to a class or partial class with import.