Why PrepareConstrainedRegions method does not work in c#? - c#

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.

Related

Is there a way to detect a C# 10 breaking change in the resolution for extension methods?

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.

c# harmony example not print injection logs as expected

the C# harmony documentation: https://github.com/pardeike/Harmony/wiki/Prioritiy-annotations
my question is that not able to run the C# harmony example successfully
the postfix annotation didn't get injection log printed as expected after Class and method get patched that i didn't see "injection logs" get printed.
c# code example below. Can someone help me find the issue
you may paste into https://dotnetfiddle.net/ to debug it.
using System;
using System.Collections.Generic;
using System.Reflection;
using Harmony;
public class Program
{
public static void Main()
{
var harmony = HarmonyInstance.Create("net.example.plugin");
harmony.PatchAll(Assembly.GetExecutingAssembly());
Program p = new Program();
p.Bar();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Program), "Bar")]
public static void Postfix_Bar(){
Console.WriteLine("postfix Bar log"); // this never gets printed as expected.
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Program), "Foo")]
public static void Postfix_Foo(ref string res){ //however, this gets error res could not be found. https://github.com/pardeike/Harmony/wiki/Prioritiy-annotations
Console.WriteLine("postfix Foo log");
res = "new value";
}
public void Bar() {
Console.WriteLine("Hello World !!! ");
}
static string Foo()
{
return "secret";
}
}
The main problem is that PatchAll() looks for classes that have at least on [HarmonyPatch] annotation. Your patches are in the class Program which does not have that annotations. This is the main problem in your example.
Solution: either annotate your Program class like this:
[HarmonyPatch]
public class Program
{
}
or create a new class that has that annotation.
The second problem I can see is that Postfix_Foo(ref string res) uses res which does not follow the documented standard for naming patch arguments. It can either be the name of an argument that the original method has (it has no arguments) or refer to the result, which requires it to be named __result.
The comment about priority annotations is misplaced too because they only apply to multiple patches to the same original method.
Finally, you call Bar() after patching which means that Foo is never called - not sure if that’s intended.

C# 7 Local Functions: are attributes / aspects allowed?

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.

C# anonymous objects in "using" statements reliable?

Imagine this snippet:
using System;
public class Report {
static int Level=0;
public static void WriteLine(string Message) {
Console.WriteLine("{0}{1}",new String(' ',4*Level),Message);
}
public class Indent:IDisposable {
public Indent() { Report.WriteLine("{"); ++Level; }
void IDisposable.Dispose() { --Level; Report.WriteLine("}"); }
}
}
class Program {
static void Main() {
Report.WriteLine("Started");
Report.WriteLine("Calling submethod");
using(new Report.Indent()) {
Report.WriteLine("Submethod started");
using(new Report.Indent()) Report.WriteLine("Subsub, maybe?");
Report.WriteLine("Submethod reporting everything is fine");
Report.WriteLine("Submethod finished");
}
Report.WriteLine("Finished");
}
}
Which produces result:
Started
Calling submethod
{
Submethod started
{
Subsub, maybe?
}
Submethod reporting everything is fine
Submethod finished
}
Finished
Inside I'm using using(new Report.Indent()) instead of sticking to the only documented version I found, i.e. using(Report.Indent r=new Report.Indent()).
In my briefer version, however, can I be sure that Dispose() will always be called on those unnamed Indent objects, every time?
P.S.
// btw, I used word "anonymous" in the title, but I'm not sure that's what new objects that aren't assigned to any named variable should be called
Yes, using enures that even "anonymous objects" are always disposed of.
Internally, using stores whatever value was used when entering the block in local variable. This stored value is disposed when exiting the block.

Extension methods conflict

Lets say I have 2 extension methods to string, in 2 different namespaces:
namespace test1
{
public static class MyExtensions
{
public static int TestMethod(this String str)
{
return 1;
}
}
}
namespace test2
{
public static class MyExtensions2
{
public static int TestMethod(this String str)
{
return 2;
}
}
}
These methods are just for example, they don't really do anything.
Now lets consider this piece of code:
using System;
using test1;
using test2;
namespace blah {
public static class Blah {
public Blah() {
string a = "test";
int i = a.TestMethod(); //Which one is chosen ?
}
}
}
The Question:
I know that only one of the extension methods will be chosen.
Which one will it be ? and why ?
Edit:
This also bothers me, but not as much because it's a static method in a static class after all:
How can I choose a certain method from a certain namespace ?
Usually I'd use Namespace.ClassNAME.Method() ... But that just beats the whole idea of extension methods. And I don't think you can use Variable.Namespace.Method()
No method will be chosen: the call is ambiguous and will not compile.
Why can't you do Namespace.ClassNAME.Method()? Certainly there is nothing that prevents you from treating extension methods as normal static methods, and in fact this is the only way for you to fix the ambiguity and have the program compile.
I had this exact question so I found this post two years later. However, I think it is important to note that this will only not compile and give the "The call is ambiguous" error if the code calling the duplicate extension methods is not in the same namespace as one of them.
If the OP were to change the namespace of his class Blah to either test1 or test2, then the code compiles, and the extension in the same namespace as the caller is used - even when both namespaces are represented in the usings. So if Blah is in the test1 namespace, "1" is returned, and if Blah is in the test2 namespace, "2" is returned.
I think this is important to add to the above answers, because I think one mainstream use-case is to have extensions in a local class library that references external extension libraries (e.g. devs share a common utility library, but have some local custom extensions that might unwittingly have the same name). By maintaining the custom local extensions in the same namespace as the code that uses them, you can maintain the extension call syntax and not have to revert to treating them as static method calls.
As Jon says, if both of these exist when you do the compilation, the compilation will just fail.
But if only one exists at the time of compilation and a external library later gets updated to add the second, the code you compiled will still continue to use the first one. This is because the compiler interally turns your code into the longhand form of calling namespace.classname.method.
I migrated big solution from .Net 4.7.1 to .Net 4.7.2. We use LINQ in our code, and we use well known and established library with name MoreLinq https://www.nuget.org/packages/morelinq/.
.Net 4.7.1 does not have .ToHashSet() methods. We used .ToHashSet() from MoreLinq library. And in the same class in the same cs-file we have both using System.Linq; and using MoreLinq;.
I retargeted a project to .Net 4.7.2 and the compiler showed The call is ambiguous error as described above. The reason was that .Net 4.7.2 added new extension methods with the same name .ToHashSet().
I cannot reimplement huge code base. I cannot replace MoreLinq with another library. This is what I did. I created a new class in a new file where I have using System.Linq; but not using MoreLinq;. This is the file (ToHashsetHelpers.cs):
using System.Collections.Generic;
using System.Linq;
namespace Common.Helpers
{
/// <summary>
/// This class with only one method helps to resolve
/// name conflict between .Net 4.7.2 and MoreLinq libraries.
///
/// .Net 4.7.2 introduced a new extension method named '.ToHashSet()'.
/// But MoreLinq already has the same method.
///
/// After migrating our solution from .Net 4.7.1 to 4.7.2
/// C# compiler shows "The call is ambiguous" error.
///
/// We cannot have both "using System.Linq;" and "using MoreLinq;" in the same C# file that
/// uses '.ToHashSet()'.
///
/// The solution is to have method with different name in a file like this.
/// </summary>
public static class ToHashsetHelpers
{
/// <summary>
/// The name of this method is ToHashset (not ToHashSet)
/// </summary>
public static HashSet<TSource> ToHashset<TSource>(this IEnumerable<TSource> source)
{
// Calling System.Linq.Enumerable.ToHashSet()
return source.ToHashSet();
}
}
}
And I renamed all .ToHashSet() to .ToHashset() in entire solution.
I was wondering about the same question and I did a quick test inside an asp.net core 6 project.
If you try this, it does not compile. Pretty much similar as other ambiguous calls or statements not involving extension methods.
using TestExtNs;
using TestExtNs2;
namespace YourBlazorProject.Server
{
public class TestMe
{
public void Test() { }
}
}
namespace TestNs
{
public static class Tester
{
public static void RunTest() // Exec this
{
var x = new YourBlazorProject.Server.TestMe();
x.Test();
x.TestExt(); // does not compile !!! error CS0121
TestExtNs.TesterExt.TestExt(x); //explicit call as working alternative
}
}
}
namespace TestExtNs
{
public static class TesterExt
{
public static void TestExt(this YourBlazorProject.Server.TestMe y)
{
Console.WriteLine("ExtNs");
System.Diagnostics.Debug.WriteLine("#> DIAG: ExtNs");
}
}
}
namespace TestExtNs2
{
public static class TesterExt
{
public static void TestExt(this YourBlazorProject.Server.TestMe y)
{
Console.WriteLine("ExtNs2");
System.Diagnostics.Debug.WriteLine("#> DIAG: ExtNs2");
}
}
}
Alternative: If there is an extension method in the same namespace, this 'closer one' is used; otherwise it won't compile.
// SomeTest.cs (example for 'closer namespace')
using TestExtNs; // This is hard REQUIREMENT for bringing the extension method from TestExtNs into scope !!!
namespace YourBlazorProject.Server
{
public class TestMe
{
public void Test() { }
}
}
namespace TestNs
{
public static class Tester
{
public static void RunTest() // Exec this
{
var x = new YourBlazorProject.Server.TestMe();
x.Test();
x.TestExt(); //Ns
TestExt(x); //Ns
TestExtNs.TesterExt.TestExt(x); //ExtNs
}
public static void TestExt(this YourBlazorProject.Server.TestMe y)
{
Console.WriteLine("Ns"); //writes to the Console Window of the *.Server.exe if its out-of-process hosted. if hosted on IISExp then its visbible in if IISExp is launched from console according to stackoverflow.
System.Diagnostics.Debug.WriteLine("#> DIAG: Ns"); //writes to the VS output console
}
}
}
namespace TestExtNs
{
public static class TesterExt
{
public static void TestExt(this YourBlazorProject.Server.TestMe y)
{
Console.WriteLine("ExtNs");
System.Diagnostics.Debug.WriteLine("#> DIAG: ExtNs");
}
}
}
Output:
Ns
Ns
ExtNs

Categories