<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="https://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>PostSharp Blog</title>
    <description>News, tips and tricks from PostSharp</description>
    <link>https://postsharp.net</link>
    <atom:link href="https://postsharp.net/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 23 Oct 2026 13:47:32 +0000</pubDate>
    <lastBuildDate>Fri, 23 Oct 2026 13:47:32 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
    
      <item>
        <title>Your Aspects, Faster: Advice Parameter Binding in PostSharp 2027.0</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2026/2026-10-advice-parameter-binding/postsharp.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;Every &lt;code class="language-plaintext highlighter-rouge"&gt;OnEntry( MethodExecutionArgs args )&lt;/code&gt; you have ever written pays three costs on every call: it allocates an object on the heap, it boxes the arguments and the return value into &lt;code class="language-plaintext highlighter-rouge"&gt;object&lt;/code&gt;, and it makes you cast them back. Our own Pattern Libraries stopped paying those costs a long time ago, because they use a mechanism we kept internal: &lt;em&gt;advice parameter binding&lt;/em&gt;. With PostSharp 2027.0, this mechanism is public, documented and checked at build time.&lt;/p&gt;

&lt;h2 id="what-methodexecutionargs-costs-you"&gt;What MethodExecutionArgs costs you&lt;/h2&gt;

&lt;p&gt;Here is a typical logging aspect written as a simple aspect. It logs the first argument, the return value, the exception and the duration of the method:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PSerializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LogAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OnMethodBoundaryAspect&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MethodExecutionArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Entering {0}({1})."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodExecutionTag&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartNew&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MethodExecutionArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"{0} returned {1}."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReturnValue&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MethodExecutionArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"{0} failed: {1}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MethodExecutionArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodExecutionTag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"{0} took {1} ms."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElapsedMilliseconds&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It works, and it has worked for many years. But look at what it asks of the runtime:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;One allocation per call.&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;MethodExecutionArgs&lt;/code&gt; lives on the heap, and so does the &lt;code class="language-plaintext highlighter-rouge"&gt;Arguments&lt;/code&gt; collection.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Boxing.&lt;/strong&gt; The &lt;code class="language-plaintext highlighter-rouge"&gt;Arguments&lt;/code&gt; collection stores the arguments with their own types, but exposes them as &lt;code class="language-plaintext highlighter-rouge"&gt;object&lt;/code&gt;, so reading an &lt;code class="language-plaintext highlighter-rouge"&gt;int&lt;/code&gt; through &lt;code class="language-plaintext highlighter-rouge"&gt;args.Arguments[0]&lt;/code&gt; boxes it. &lt;code class="language-plaintext highlighter-rouge"&gt;args.ReturnValue&lt;/code&gt; is an &lt;code class="language-plaintext highlighter-rouge"&gt;object&lt;/code&gt; too, so an &lt;code class="language-plaintext highlighter-rouge"&gt;int&lt;/code&gt; return value is boxed as well.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Casts.&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;MethodExecutionTag&lt;/code&gt; is an &lt;code class="language-plaintext highlighter-rouge"&gt;object&lt;/code&gt; too, so the stopwatch must be cast back, and a wrong cast fails at run time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a method called a few times per request, nobody notices. On a hot path, the aspect becomes the most expensive part of the method.&lt;/p&gt;

&lt;h2 id="the-same-aspect-with-bound-parameters"&gt;The same aspect with bound parameters&lt;/h2&gt;

&lt;p&gt;With advice parameter binding, the aspect derives from &lt;code class="language-plaintext highlighter-rouge"&gt;MethodLevelAspect&lt;/code&gt;, and each advice method declares only the values it needs, as typed parameters. A &lt;em&gt;binding attribute&lt;/em&gt; on each parameter says where the value comes from:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PSerializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LogAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MethodLevelAspect&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OnMethodEntryAdvice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DeclarationName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;StateScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodInvocation&lt;/span&gt; &lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;Stopwatch&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Entering {0}({1})."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;stopwatch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartNew&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OnMethodSuccessAdvice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;OnSuccess&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DeclarationName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ReturnValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;returnValue&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"{0} returned {1}."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;returnValue&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OnMethodExceptionAdvice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DeclarationName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"{0} failed: {1}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OnMethodExitAdvice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DeclarationName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;StateScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodInvocation&lt;/span&gt; &lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;Stopwatch&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"{0} took {1} ms."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElapsedMilliseconds&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The bindings in this example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;[DeclarationName]&lt;/code&gt;&lt;/strong&gt; gives the name of the method. It is a constant, computed at build time, so there is no reflection call at run time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;[State]&lt;/code&gt;&lt;/strong&gt; declares a typed variable shared by the advices for one execution of the method. The entry advice assigns it with &lt;code class="language-plaintext highlighter-rouge"&gt;out&lt;/code&gt;, and the exit advice reads it. There is no &lt;code class="language-plaintext highlighter-rouge"&gt;MethodExecutionTag&lt;/code&gt; and no cast.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;[ReturnValue]&lt;/code&gt;&lt;/strong&gt; on a generic parameter &lt;code class="language-plaintext highlighter-rouge"&gt;T&lt;/code&gt; receives the return value without boxing. PostSharp infers &lt;code class="language-plaintext highlighter-rouge"&gt;T&lt;/code&gt; for each target method.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;[Argument( 0 )]&lt;/code&gt;&lt;/strong&gt; reads the first argument. Here it is still boxed, because the parameter is &lt;code class="language-plaintext highlighter-rouge"&gt;object&lt;/code&gt;. Make the advice generic to avoid it, as shown below.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;[Exception]&lt;/code&gt;&lt;/strong&gt; gives the exception, typed as &lt;code class="language-plaintext highlighter-rouge"&gt;Exception&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The aspect is applied exactly as before, with &lt;code class="language-plaintext highlighter-rouge"&gt;[Log]&lt;/code&gt; on a method or with multicasting.&lt;/p&gt;

&lt;h2 id="what-postsharp-actually-generates"&gt;What PostSharp actually generates&lt;/h2&gt;

&lt;p&gt;Bound parameters are a build-time contract, so the woven code passes the values directly. Take a smaller profiling aspect, with an entry advice that starts a &lt;code class="language-plaintext highlighter-rouge"&gt;[State]&lt;/code&gt; stopwatch and an exit advice that prints the &lt;code class="language-plaintext highlighter-rouge"&gt;[DeclarationName]&lt;/code&gt; and the duration (see &lt;a href="https://doc.postsharp.net/postsharp/preview/framework/aspects/member-level-aspects/advices"&gt;Aspect Development Concepts&lt;/a&gt;). PostSharp transforms the target method into code equivalent to this:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Stopwatch&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;aspect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Method body.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;aspect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"ProfileAspect.OrderService.PlaceOrder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The state is a local variable, the method name is a string literal, and the advices are plain method calls. Apart from the &lt;code class="language-plaintext highlighter-rouge"&gt;Stopwatch&lt;/code&gt; itself, the woven code allocates nothing.&lt;/p&gt;

&lt;h2 id="generic-advices-inferred-for-each-target"&gt;Generic advices, inferred for each target&lt;/h2&gt;

&lt;p&gt;An advice method can be generic. PostSharp infers the type arguments separately for each target method, with rules similar to the type inference of the C# compiler, and then calls a different instantiation of the advice from each target.&lt;/p&gt;

&lt;p&gt;Take the &lt;code class="language-plaintext highlighter-rouge"&gt;OnSuccess&amp;lt;T&amp;gt;&lt;/code&gt; advice above. On &lt;code class="language-plaintext highlighter-rouge"&gt;int Square( int value )&lt;/code&gt;, PostSharp calls &lt;code class="language-plaintext highlighter-rouge"&gt;OnSuccess&amp;lt;int&amp;gt;&lt;/code&gt;. On &lt;code class="language-plaintext highlighter-rouge"&gt;string Format( Order order )&lt;/code&gt;, it calls &lt;code class="language-plaintext highlighter-rouge"&gt;OnSuccess&amp;lt;string&amp;gt;&lt;/code&gt;. The same source method serves every signature, and an &lt;code class="language-plaintext highlighter-rouge"&gt;int&lt;/code&gt; stays an &lt;code class="language-plaintext highlighter-rouge"&gt;int&lt;/code&gt; all the way.&lt;/p&gt;

&lt;p&gt;Inference also works through constructed types and constraints:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OnMethodEntryAdvice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;OnEntry&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DeclarationName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IComparable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"{0} received {1} items, the first is {2}."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Applied to a method that takes a &lt;code class="language-plaintext highlighter-rouge"&gt;List&amp;lt;int&amp;gt;&lt;/code&gt;, this advice gets &lt;code class="language-plaintext highlighter-rouge"&gt;T = int&lt;/code&gt;: PostSharp matches &lt;code class="language-plaintext highlighter-rouge"&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; against the type of the argument, its base types and its interfaces, as C# would. The rules, in short:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No boxing.&lt;/strong&gt; A value of a value type is passed as it is, and a &lt;code class="language-plaintext highlighter-rouge"&gt;ref&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;out&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;in&lt;/code&gt; parameter receives the location itself.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Common type.&lt;/strong&gt; When several bound values give a type argument, it is the type to which all of them can be assigned. &lt;code class="language-plaintext highlighter-rouge"&gt;OnEntry&amp;lt;T&amp;gt;( [Argument( 0 )] T a, [Argument( 1 )] T b )&lt;/code&gt; on &lt;code class="language-plaintext highlighter-rouge"&gt;M( string, object )&lt;/code&gt; gives &lt;code class="language-plaintext highlighter-rouge"&gt;T = object&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Constructed types.&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;T[]&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;T?&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;IList&amp;lt;T&amp;gt;&lt;/code&gt; and the like are matched against the value’s type, its base types and its interfaces.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Constraints are checked.&lt;/strong&gt; Including &lt;code class="language-plaintext highlighter-rouge"&gt;unmanaged&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;new()&lt;/code&gt; and constraints that refer to other type parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a type argument cannot be inferred for a target, or violates a constraint, the build fails with the error LA0220, which states the reason. Apply the advice above to a method that takes a &lt;code class="language-plaintext highlighter-rouge"&gt;List&amp;lt;object&amp;gt;&lt;/code&gt;, and you learn at build time that &lt;code class="language-plaintext highlighter-rouge"&gt;object&lt;/code&gt; does not implement &lt;code class="language-plaintext highlighter-rouge"&gt;IComparable&amp;lt;object&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The type of a parameter can also use the type parameters of a generic aspect class. They are replaced by the type arguments of the aspect instance. See &lt;a href="https://doc.postsharp.net/postsharp/preview/framework/aspects/member-level-aspects/advice-parameter-binding#generic-advice-methods"&gt;Generic advice methods&lt;/a&gt; for the complete rules.&lt;/p&gt;

&lt;h2 id="errors-at-build-time-not-in-production"&gt;Errors at build time, not in production&lt;/h2&gt;

&lt;p&gt;A simple aspect accepts any method, then fails at run time if it makes a wrong assumption, for instance by reading &lt;code class="language-plaintext highlighter-rouge"&gt;args.Arguments[0]&lt;/code&gt; on a method that has no parameters.&lt;/p&gt;

&lt;p&gt;With bound parameters, PostSharp checks each advice against each target when it weaves the aspect. An advice that does not fit a target is a build error with a message code. Apply the &lt;code class="language-plaintext highlighter-rouge"&gt;LogAttribute&lt;/code&gt; above to a method without parameters, and &lt;code class="language-plaintext highlighter-rouge"&gt;[Argument( 0 )]&lt;/code&gt; causes the error LA0225: the target method has no parameter at this index. A parameter of the wrong type, a binding used in an advice that does not support it, or a type argument that cannot be inferred all have their own codes. PostSharp reports all the errors of an aspect in one build.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://doc.postsharp.net/postsharp/preview/framework/aspects/member-level-aspects/advice-parameter-binding#build-messages"&gt;complete list of build messages&lt;/a&gt; is in the reference.&lt;/p&gt;

&lt;h2 id="beyond-the-basics"&gt;Beyond the basics&lt;/h2&gt;

&lt;p&gt;The logging example uses five bindings. The &lt;a href="https://doc.postsharp.net/postsharp/preview/framework/aspects/member-level-aspects/advice-parameter-binding"&gt;Advice Parameter Reference&lt;/a&gt; lists them all. Among them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Write access with &lt;code class="language-plaintext highlighter-rouge"&gt;ref&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;out&lt;/code&gt;.&lt;/strong&gt; Replace an argument, set or replace the return value, or replace the exception.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;[FlowBehavior]&lt;/code&gt;&lt;/strong&gt; to return without executing the method, or to swallow an exception.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Async methods.&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;[CurrentTask]&lt;/code&gt; gives the task that the caller observes, including for &lt;code class="language-plaintext highlighter-rouge"&gt;ValueTask&lt;/code&gt; methods, and &lt;code class="language-plaintext highlighter-rouge"&gt;[AsyncCallId]&lt;/code&gt; identifies one execution of the async method.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Interception.&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;[Binding]&lt;/code&gt; gives an object that invokes the original method, field or property.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;[This]&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;[Declaration]&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;[DeclarationIdentifier]&lt;/code&gt;&lt;/strong&gt; for the instance and the reflection object of the target.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grouping also got simpler. &lt;code class="language-plaintext highlighter-rouge"&gt;[SelfPointcut]&lt;/code&gt; and the &lt;code class="language-plaintext highlighter-rouge"&gt;Master&lt;/code&gt; property are optional when the grouping of the advices is unambiguous, so an aspect that applies to a single method, field, property or event needs neither.&lt;/p&gt;

&lt;h2 id="should-you-modernize-your-aspects"&gt;Should you modernize your aspects?&lt;/h2&gt;

&lt;p&gt;Not necessarily. Simple aspects remain fully supported, and you can keep the &lt;code class="language-plaintext highlighter-rouge"&gt;AdviceArgs&lt;/code&gt; signature in the following cases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The aspect intercepts events, validates fields or parameters, or uses &lt;code class="language-plaintext highlighter-rouge"&gt;OnInvokeAsync&lt;/code&gt;. These advices do not accept bound parameters yet.&lt;/li&gt;
  &lt;li&gt;The aspect reads the value yielded by an iterator, which only &lt;code class="language-plaintext highlighter-rouge"&gt;MethodExecutionArgs.YieldValue&lt;/code&gt; gives.&lt;/li&gt;
  &lt;li&gt;The aspect is not on a performance-sensitive path, and you prefer the simpler programming model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other &lt;a href="https://doc.postsharp.net/postsharp/preview/framework/aspects/member-level-aspects/advice-parameter-binding#limitations"&gt;limitations&lt;/a&gt; are listed in the reference.&lt;/p&gt;

&lt;p&gt;For the aspects that do run on hot paths, the conversion is mechanical. Change the base class to &lt;code class="language-plaintext highlighter-rouge"&gt;MethodLevelAspect&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;LocationLevelAspect&lt;/code&gt;, turn each override into an advice method with the matching attribute, replace each &lt;code class="language-plaintext highlighter-rouge"&gt;args&lt;/code&gt; member with a bound parameter, and build. PostSharp tells you what does not fit. The &lt;a href="https://doc.postsharp.net/postsharp/preview/framework/aspects/member-level-aspects/modernizing-aspects"&gt;Modernizing Your Custom Aspects&lt;/a&gt; article gives the steps and a table that maps each member of &lt;code class="language-plaintext highlighter-rouge"&gt;MethodExecutionArgs&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;MethodInterceptionArgs&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;LocationInterceptionArgs&lt;/code&gt; to its binding.&lt;/p&gt;

&lt;h2 id="try-it"&gt;Try it&lt;/h2&gt;

&lt;p&gt;Advice parameter binding is available from PostSharp 2027.0, which also adds support for .NET 11 and C# 15. See &lt;a href="https://doc.postsharp.net/postsharp/preview/introduction/whats-new/2027-0"&gt;What’s New in PostSharp 2027.0&lt;/a&gt; for the full list. Pick one aspect on your hottest path, convert it, and tell us how it went on &lt;a href="https://github.com/postsharp/PostSharp.Public/issues"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy meta-programming with PostSharp!&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/postsharp-advice-parameter-binding"&gt;Your Aspects, Faster: Advice Parameter Binding in PostSharp 2027.0&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Fri, 23 Oct 2026 07:00:00 +0000</pubDate>
        
        <link>https://postsharp.net/blog/postsharp-advice-parameter-binding</link>
        <guid isPermaLink="true">https://postsharp.net/blog/postsharp-advice-parameter-binding</guid>
        
        
        <category>PostSharp</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2026/2026-10-advice-parameter-binding/postsharp.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>Security and Privacy Update for PostSharp and Metalama</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2026/2026-06-security/metalama.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;Today we are shipping a coordinated security and privacy update across every supported branch of PostSharp and Metalama. It covers the build toolchain, the design time services, telemetry, and the Visual Studio Tools. It does not change the public API or how you write aspects, and none of it reaches your compiled application: every issue is in tooling that runs on developer and build machines, so the applications you have already built and shipped are not affected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; All fixes are already shipped on every supported branch. The most serious issues (three High-severity advisories) require an attacker to already have a local account on your machine, so they mainly affect shared or multi-user build agents. Upgrade when convenient; prioritize it if you build on shared agents. Nothing else to do.&lt;/p&gt;

&lt;h2 id="scope-and-attack-vectors"&gt;Scope and attack vectors&lt;/h2&gt;

&lt;p&gt;We published GitHub Security Advisories for the most severe findings, all rated High. They require the attacker to already have local access to the same machine, under a different user account, which mainly matters on shared or multi-user build agents.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PostSharp&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;Local code execution, information disclosure, and denial of service in the build toolchain on shared machines (&lt;a href="https://github.com/postsharp/PostSharp.Public/security/advisories/GHSA-gpwg-4q4j-792q"&gt;GHSA-gpwg-4q4j-792q&lt;/a&gt;)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Metalama&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;Unsafe deserialization over the Visual Studio named pipe enables local code execution (&lt;a href="https://github.com/metalama/Metalama/security/advisories/GHSA-h26j-4vp7-x9w2"&gt;GHSA-h26j-4vp7-x9w2&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;Local code execution via DLL planting in world-writable temp directories on Linux/macOS (&lt;a href="https://github.com/metalama/Metalama/security/advisories/GHSA-m6wq-39m7-9xpp"&gt;GHSA-m6wq-39m7-9xpp&lt;/a&gt;)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We found and fixed other vulnerabilities that required the attacker to sit between your device and our &lt;code class="language-plaintext highlighter-rouge"&gt;postsharp.net&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;metalama.net&lt;/code&gt; servers as a man-in-the-middle. See the GitHub release notes in the &lt;em&gt;What changed&lt;/em&gt; section below.&lt;/p&gt;

&lt;p&gt;None of these vulnerabilities could be exploited by a remote attacker against a normal developer or CI setup.&lt;/p&gt;

&lt;p&gt;One assumption is unchanged: like the .NET SDK itself, PostSharp and Metalama assume your source code and NuGet dependencies are trusted, and they execute that code at build time. This review hardened everything around that boundary, but it does not sandbox hostile source or malicious packages. Vetting what enters your build remains your job.&lt;/p&gt;

&lt;p&gt;There is no known exploitation, and no customer needs to do anything beyond upgrading when convenient.&lt;/p&gt;

&lt;h2 id="why-now-and-not-sooner"&gt;Why now, and not sooner&lt;/h2&gt;

&lt;p&gt;No attacker or customer reported these issues. We found and disclosed them ourselves, and published the advisories with the fixes already shipped on every supported branch.&lt;/p&gt;

&lt;p&gt;Why now? Because the new generation of AI models makes it both possible and urgent. Auditing a 20-year-old living codebase used to mean weeks of work and skills we didn’t have in-house. It did not seem necessary for a niche tool that processes trusted local inputs without network access. However, with new AI tools, an in-depth security analysis takes a couple of hours, both for the publisher and the attacker. We used Claude Opus 4.8 for the review, reviewed every finding, supervised and refined the fixes, and tested them on real VMs before release. The whole pass still took weeks; without the model it would not have happened.&lt;/p&gt;

&lt;h2 id="run-the-analysis-yourself"&gt;Run the analysis yourself&lt;/h2&gt;

&lt;p&gt;We believe that security stems from transparency. Ultimately, it does not matter &lt;em&gt;who&lt;/em&gt; wrote the code you execute. What matters is the code itself.&lt;/p&gt;

&lt;p&gt;Metalama is open source: clone it from &lt;a href="https://github.com/metalama/Metalama"&gt;GitHub&lt;/a&gt; and point your own tools at it. PostSharp’s source is available to enterprise customers at no additional charge under a source-available license, going back to version 1.0; see &lt;a href="/blog/postsharp-source-available"&gt;PostSharp Is Now Source Available&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you run your own analysis, report any defects privately through GitHub Security Advisories (&lt;a href="https://github.com/metalama/Metalama/security/advisories"&gt;Metalama&lt;/a&gt;, &lt;a href="https://github.com/postsharp/PostSharp.Public/security/advisories"&gt;PostSharp&lt;/a&gt;) and we will handle them the way we handled this review. Our &lt;a href="https://www.postsharp.net/compliance/security"&gt;security policy&lt;/a&gt; describes how we receive, prioritize, and disclose vulnerabilities.&lt;/p&gt;

&lt;h2 id="what-changed"&gt;What changed&lt;/h2&gt;

&lt;p&gt;Across all these releases, the work falls into a few themes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Safer behavior on shared machines.&lt;/strong&gt; Caches, temp files, and local servers now stay scoped to the current user, so one user cannot interfere with or read another’s build.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Telemetry hardened to minimize what leaves your machine.&lt;/strong&gt; Crash reports now omit exception messages and data, and do a more thorough job scrubbing both secrets and confidential identifiers, such as a company name embedded in a namespace, so a stack trace can’t carry a token, a connection string, or your internal names off your machine. We rotate device IDs monthly and keep separate salts for our own diagnostics and our third-party analytics, so the two are much harder to correlate. For exactly what we collect and how to turn it off, see our &lt;a href="https://www.postsharp.net/company/legal/privacy-policy"&gt;privacy policy&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Removed features that collected data.&lt;/strong&gt; We took out the in-product extras that came with a data cost: the Learning Hub tool window in Visual Studio, the Areas of Interest selection, and newsletter and email registration. We also removed the telemetry code for the long-defunct per-usage licensing model, which used to upload reversibly-hashed type names.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Defense in depth against our own servers.&lt;/strong&gt; Some data comes from postsharp.net or metalama.net, such as the news feed and content feeds. We hardened the parsers and serializers that handle it, so a compromise of our servers could not turn that data into code execution on your machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The LTS and latest branches get the full set of changes. The two Metalama advisory fixes were also backported to the other supported versions, 2025.1.18 and 2026.0.25, without the broader privacy and telemetry rework. See the &lt;a href="https://www.postsharp.net/support/policies/versions"&gt;version status page&lt;/a&gt; for what is still serviced.&lt;/p&gt;

&lt;p&gt;The per-release notes have the full technical detail:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PostSharp&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href="https://github.com/postsharp/PostSharp.Public/releases/tag/v2026.0.13"&gt;2026.0.13&lt;/a&gt; (Current)&lt;/li&gt;
      &lt;li&gt;&lt;a href="https://github.com/postsharp/PostSharp.Public/releases/tag/v2024.0.23"&gt;2024.0.23&lt;/a&gt; (LTS)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Metalama&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release/2026.1.19"&gt;2026.1.19&lt;/a&gt; (Current)&lt;/li&gt;
      &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release/2026.0.25"&gt;2026.0.25&lt;/a&gt; (Extended Support: advisory fixes only)&lt;/li&gt;
      &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release/2025.1.18"&gt;2025.1.18&lt;/a&gt; (Extended Support: advisory fixes only)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Visual Studio Tools for PostSharp and Metalama&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama.Vsx.Public/releases/tag/release/2026.1.5"&gt;2026.1.5&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All packages are on &lt;a href="https://www.nuget.org/packages/Metalama.Framework"&gt;NuGet.org&lt;/a&gt;, and the extension is on the &lt;a href="https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp"&gt;Visual Studio Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="updated-policies"&gt;Updated policies&lt;/h2&gt;

&lt;p&gt;As part of this review we updated our &lt;a href="https://www.postsharp.net/company/legal/privacy-policy"&gt;privacy policy&lt;/a&gt; to describe exactly what we collect, and our &lt;a href="https://www.postsharp.net/compliance/security"&gt;security policy&lt;/a&gt; to reflect the current threat model and how to report a vulnerability.&lt;/p&gt;

&lt;h2 id="how-to-upgrade"&gt;How to upgrade&lt;/h2&gt;

&lt;p&gt;For most users this is a drop-in upgrade: bump the NuGet versions and update the extension. No API changes, nothing to reconfigure. Open-source and free-edition users can upgrade freely; for commercial editions, upgrading requires an active maintenance subscription. If you build on shared or multi-user agents, prioritize it.&lt;/p&gt;

&lt;h2 id="whats-next"&gt;What’s next&lt;/h2&gt;

&lt;p&gt;The next Metalama and PostSharp feature releases will focus on .NET 11 support, with work starting in late August.&lt;/p&gt;

&lt;p&gt;Found an issue or have a feature request? Let us know on &lt;a href="https://github.com/metalama/Metalama/issues"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy meta-programming!&lt;/p&gt;

&lt;p&gt;-gael&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/security-privacy-hardening-2026-06"&gt;Security and Privacy Update for PostSharp and Metalama&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Tue, 30 Jun 2026 19:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/security-privacy-hardening-2026-06</link>
        <guid isPermaLink="true">https://postsharp.net/blog/security-privacy-hardening-2026-06</guid>
        
        <category>featured</category>
        
        
        <category>PostSharp</category>
        
        <category>Metalama</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2026/2026-06-security/metalama.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>Metalama 2026.1 Generally Available: First LTS Release, Complete C# 14 Support, and a Redesigned Redis Backend</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2026/2026-06-announcement/metalama.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;Metalama 2026.1 is now generally available, and it’s our first &lt;strong&gt;long-term support (LTS)&lt;/strong&gt; release. This is a consolidation release: we finished the C# 14 work we started in 2026.0, extended the advising API, redesigned the Redis caching backend, cut several third-party dependencies, and closed most of the bug backlog.&lt;/p&gt;

&lt;p&gt;The packages are on &lt;a href="https://www.nuget.org/packages/Metalama.Framework"&gt;NuGet.org&lt;/a&gt;, and the Visual Studio extension is on the &lt;a href="https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp"&gt;Visual Studio Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the complete list of changes, see the &lt;a href="https://doc.metalama.net/conceptual/release-notes/release-notes-2026-1"&gt;release notes&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="complete-c-14-support-in-aspects"&gt;Complete C# 14 support in aspects&lt;/h2&gt;

&lt;p&gt;In 2026.0 we supported all C# 14 features in your own code but left a few gaps in aspects. Those gaps are now closed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Introducing extension blocks.&lt;/strong&gt; The new &lt;a href="https://doc.metalama.net/api/metalama-framework-aspects-adviserextensions-introduceextensionblock"&gt;IntroduceExtensionBlock&lt;/a&gt; advice lets aspects add extension blocks to static classes. See &lt;a href="https://doc.metalama.net/conceptual/aspects/advising/introducing-types"&gt;Introducing types&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Contracts on extension-block receiver parameters.&lt;/strong&gt; You can now add a contract to the receiver parameter of an extension block, and it propagates across the extension members.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Introducing user-defined compound assignment operators.&lt;/strong&gt; Operators such as &lt;code class="language-plaintext highlighter-rouge"&gt;+=&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;-=&lt;/code&gt;, and &lt;code class="language-plaintext highlighter-rouge"&gt;*=&lt;/code&gt; can now be introduced through &lt;a href="https://doc.metalama.net/conceptual/aspects/advising/introducing-members"&gt;IntroduceMethod&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run-time invoker interfaces for extension members.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Introducing parameters into partial constructors.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two C# 14 constructs remain unsupported &lt;em&gt;inside templates&lt;/em&gt;: null-conditional assignments (&lt;code class="language-plaintext highlighter-rouge"&gt;x?.P = v&lt;/code&gt;) and the &lt;code class="language-plaintext highlighter-rouge"&gt;field&lt;/code&gt; keyword. Rather than fail confusingly, Metalama now detects them and reports a clear diagnostic (&lt;code class="language-plaintext highlighter-rouge"&gt;LAMA0101&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id="initialization-advice"&gt;Initialization advice&lt;/h2&gt;

&lt;p&gt;We’ve extended the &lt;a href="https://doc.metalama.net/conceptual/aspects/advising/initializers"&gt;initialization advice&lt;/a&gt; with two new initializer kinds and better support for records:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;AfterLastInstanceConstructor&lt;/code&gt;&lt;/strong&gt; injects code into a &lt;code class="language-plaintext highlighter-rouge"&gt;protected virtual OnConstructed&lt;/code&gt; method, so it runs once after the most-derived constructor completes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;AfterObjectInitializer&lt;/code&gt;&lt;/strong&gt; runs after the constructor and any object initializer or &lt;code class="language-plaintext highlighter-rouge"&gt;with&lt;/code&gt; expression have completed. Metalama makes the target implement &lt;code class="language-plaintext highlighter-rouge"&gt;IInitializable&lt;/code&gt; and rewrites the call sites accordingly, so you can validate or compute derived state once every property has been set.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;BeforeInstanceConstructor&lt;/code&gt;&lt;/strong&gt; now works on positional records with primary constructors.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href="https://doc.metalama.net/conceptual/aspects/advising/introducing-constructor-parameters"&gt;Constructor parameter introduction&lt;/a&gt;&lt;/strong&gt; can now generate forwarding overloads, so you can add a parameter without breaking binary compatibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="dependency-injection"&gt;Dependency injection&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://doc.metalama.net/api/metalama-framework-advising-pullstrategy-introduceparameterandpull"&gt;IntroduceParameterAndPull&lt;/a&gt; now accepts a &lt;code class="language-plaintext highlighter-rouge"&gt;reuseExistingParameterOfCompatibleType&lt;/code&gt; argument. When a chained constructor already accepts a parameter of the same or a more specific type, the existing parameter is forwarded instead of duplicated. The default .NET DI adapter relies on this: an aspect that pulls an &lt;code class="language-plaintext highlighter-rouge"&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt; no longer adds a second &lt;code class="language-plaintext highlighter-rouge"&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt; parameter when the target constructor already has one. See &lt;a href="https://doc.metalama.net/conceptual/aspects/dependency-injection"&gt;Injecting dependencies into aspects&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="redesigned-redis-caching-backend"&gt;Redesigned Redis caching backend&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://doc.metalama.net/patterns/caching/redis"&gt;Metalama.Patterns.Caching.Backends.Redis&lt;/a&gt; received a major overhaul, porting the work we did for PostSharp 2025.1 (see &lt;a href="/blog/redis-caching-update"&gt;Breaking Update of PostSharp Caching adapter for Redis in 2025.1&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;New data schema.&lt;/strong&gt; Reading a cache item is now a single, observationally-consistent operation, and cache keys are versioned.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cluster support.&lt;/strong&gt; Hash tags keep related keys on the same shard, and you can configure &lt;code class="language-plaintext highlighter-rouge"&gt;ReadCommandFlags&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;WriteCommandFlags&lt;/code&gt; for multi-node setups.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Retry and exception-handling policies.&lt;/strong&gt; The new &lt;code class="language-plaintext highlighter-rouge"&gt;IRetryPolicy&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;IExceptionHandlingPolicy&lt;/code&gt; interfaces give you exponential backoff and centralized error handling.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Key compression.&lt;/strong&gt; Keys longer than a configurable threshold (128 characters by default) are hashed automatically with &lt;code class="language-plaintext highlighter-rouge"&gt;XxHash64&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;XxHash128&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Overload detection.&lt;/strong&gt; The &lt;code class="language-plaintext highlighter-rouge"&gt;IsBackgroundTaskQueueOverloaded&lt;/code&gt; property lets you back off under pressure, and the dependency garbage collector pauses itself when the queue is saturated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use the Redis backend, note that the schema change requires purging the cache (&lt;code class="language-plaintext highlighter-rouge"&gt;FLUSHDB&lt;/code&gt;) on upgrade. See the breaking changes below.&lt;/p&gt;

&lt;h2 id="fewer-third-party-dependencies"&gt;Fewer third-party dependencies&lt;/h2&gt;

&lt;p&gt;We reduced our dependency surface to improve supply-chain safety:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;System.Text.Json&lt;/code&gt; replaces &lt;code class="language-plaintext highlighter-rouge"&gt;Newtonsoft.Json&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;System.IO.Hashing&lt;/code&gt; replaces &lt;code class="language-plaintext highlighter-rouge"&gt;K4os.Hash&lt;/code&gt; (the xxHash implementation now comes from the .NET runtime).&lt;/li&gt;
  &lt;li&gt;Diff-tool integration moved to the optional &lt;code class="language-plaintext highlighter-rouge"&gt;Metalama.Extensions.DiffEngine&lt;/code&gt; package, and HTML test output moved to &lt;code class="language-plaintext highlighter-rouge"&gt;Metalama.Extensions.HtmlWriter&lt;/code&gt; (both used in &lt;a href="https://doc.metalama.net/conceptual/aspects/testing/snapshot-testing"&gt;aspect snapshot testing&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;We no longer use MD5 for non-cryptographic hashing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="performance-improvements"&gt;Performance improvements&lt;/h2&gt;

&lt;p&gt;This release improves both build-time and design-time performance. At build time, configuration files now use &lt;code class="language-plaintext highlighter-rouge"&gt;System.Text.Json&lt;/code&gt; source generators, polymorphic pattern matching in the code model was restructured to match on &lt;code class="language-plaintext highlighter-rouge"&gt;DeclarationKind&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;TypeKind&lt;/code&gt;, and the WPF temporary compilation phase (&lt;code class="language-plaintext highlighter-rouge"&gt;MarkupCompilePass1&lt;/code&gt;) runs a reduced pipeline that emits member signatures only and skips the linker step. At design time, the protocol between the analyzer and the design-time services moved from JSON to MessagePack.&lt;/p&gt;

&lt;h2 id="bug-fixes"&gt;Bug fixes&lt;/h2&gt;

&lt;p&gt;We fixed over 100 bugs in this release, spanning the template engine, linker, code model, design-time services, dependency injection, and diagnostics. Metalama 2026.1 is the first release ever to ship with (almost) zero known bugs.&lt;/p&gt;

&lt;h2 id="breaking-changes"&gt;Breaking changes&lt;/h2&gt;

&lt;p&gt;A few changes may affect existing code:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;AddInitializer&lt;/code&gt; ordering now respects &lt;code class="language-plaintext highlighter-rouge"&gt;AspectOrderDirection.RunTime&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;IntroduceParameter&lt;/code&gt; on a record primary constructor no longer materializes the parameter by default; opt in with &lt;code class="language-plaintext highlighter-rouge"&gt;materializeOnRecord&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;IConstructor.InitializerKind&lt;/code&gt; returns &lt;code class="language-plaintext highlighter-rouge"&gt;Base&lt;/code&gt; (instead of &lt;code class="language-plaintext highlighter-rouge"&gt;None&lt;/code&gt;) for an implicit &lt;code class="language-plaintext highlighter-rouge"&gt;base()&lt;/code&gt; call.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;IEvent.RaiseMethod&lt;/code&gt; returns &lt;code class="language-plaintext highlighter-rouge"&gt;null&lt;/code&gt; for non-raisable events. Use &lt;code class="language-plaintext highlighter-rouge"&gt;IEventInvoker.CanRaise&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The test base classes &lt;code class="language-plaintext highlighter-rouge"&gt;AspectTestClass&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;DefaultAspectTestClass&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;CurrentDirectoryAttribute&lt;/code&gt;, and &lt;code class="language-plaintext highlighter-rouge"&gt;CurrentProjectAttribute&lt;/code&gt; were removed.&lt;/li&gt;
  &lt;li&gt;For the Redis backend: the schema redesign requires a cache purge, &lt;code class="language-plaintext highlighter-rouge"&gt;ExceptionHandlingCachingBackendEnhancer&lt;/code&gt; was removed in favor of the &lt;code class="language-plaintext highlighter-rouge"&gt;ExceptionHandlingPolicy&lt;/code&gt; property, and &lt;code class="language-plaintext highlighter-rouge"&gt;TransactionMaxRetries&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;CacheCleanupOptions.Sequential&lt;/code&gt; are obsolete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the &lt;a href="https://doc.metalama.net/conceptual/release-notes/release-notes-2026-1#breaking-changes"&gt;release notes&lt;/a&gt; for the full list.&lt;/p&gt;

&lt;h2 id="whats-next"&gt;What’s next&lt;/h2&gt;

&lt;p&gt;As our first LTS release, 2026.1 is the version to standardize on if you want stability. The next feature release is expected to be 2027.0, with support for .NET 11. Work will start in late August, and we don’t expect any larger work on Metalama in the meantime.&lt;/p&gt;

&lt;p&gt;Found an issue or have a feature request? Let us know on &lt;a href="https://github.com/metalama/Metalama/issues"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy meta-programming!&lt;/p&gt;

&lt;p&gt;-gael&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/metalama-2026-1-ga"&gt;Metalama 2026.1 Generally Available: First LTS Release, Complete C# 14 Support, and a Redesigned Redis Backend&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Wed, 03 Jun 2026 07:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/metalama-2026-1-ga</link>
        <guid isPermaLink="true">https://postsharp.net/blog/metalama-2026-1-ga</guid>
        
        <category>featured</category>
        
        
        <category>Metalama</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2026/2026-06-announcement/metalama.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>PostSharp 2026.0 Generally Available: Support for .NET 10, C# 14 and Extension Blocks</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2026/2026-01-announcement/postsharp.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;Alongside &lt;a href="/blog/metalama-2026-0-ga"&gt;Metalama 2026.0&lt;/a&gt;, we’re announcing the general availability of PostSharp 2026.0. This release brings full support for .NET 10 and C# 14, including proper handling of the new extension blocks syntax introduced in C# 14.&lt;/p&gt;

&lt;h2 id="net-10-and-c-14"&gt;.NET 10 and C# 14&lt;/h2&gt;

&lt;p&gt;We’ve updated and tested PostSharp to fully support the .NET 10 SDK and C# 14 across all packages. You can now confidently use PostSharp with the latest .NET stack.&lt;/p&gt;

&lt;h2 id="extension-blocks-support"&gt;Extension Blocks Support&lt;/h2&gt;

&lt;p&gt;C# 14 introduces extension blocks as an alternative syntax for defining extension methods. This new language feature required careful consideration in PostSharp to ensure predictable behavior.&lt;/p&gt;

&lt;p&gt;By default, multicast attributes now skip extension block members. This prevents aspects from being unintentionally applied to compiler-generated metadata associated with extension blocks.&lt;/p&gt;

&lt;p&gt;For scenarios where you do want to target extension block members, we’ve added the &lt;code class="language-plaintext highlighter-rouge"&gt;AllowExtensionBlockMembers&lt;/code&gt; property to both &lt;code class="language-plaintext highlighter-rouge"&gt;MulticastAttribute&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;MulticastAttributeUsageAttribute&lt;/code&gt;. Setting this property to &lt;code class="language-plaintext highlighter-rouge"&gt;true&lt;/code&gt; enables your aspects to target extension block members explicitly.&lt;/p&gt;

&lt;p&gt;Additionally, developers using &lt;code class="language-plaintext highlighter-rouge"&gt;IAspectProvider&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;IAdviceProvider&lt;/code&gt; interfaces can use the new &lt;code class="language-plaintext highlighter-rouge"&gt;ReflectionHelper.IsExtensionBlockMetadata&lt;/code&gt; utility method to identify compiler-generated metadata associated with extension blocks.&lt;/p&gt;

&lt;h2 id="updated-framework-baselines"&gt;Updated Framework Baselines&lt;/h2&gt;

&lt;p&gt;We’ve raised the minimum supported frameworks to reflect the current .NET ecosystem:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;.NET Standard 2.0&lt;/strong&gt; (minimum)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;.NET Framework 4.7.0&lt;/strong&gt; (minimum)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;.NET 6.0&lt;/strong&gt; (minimum for .NET Core-based projects)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All pre-2017 target frameworks and obsolete .NET Core versions that have lost mainstream support have been removed. Please consult the &lt;a href="https://doc.postsharp.net/introduction/whatsnew/whats-new-20260"&gt;breaking changes documentation&lt;/a&gt; for complete migration guidance.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;PostSharp 2026.0 continues our commitment to keeping PostSharp compatible with the latest .NET releases. While our new feature development is focused on &lt;a href="/blog/metalama-2026-0-ga"&gt;Metalama&lt;/a&gt;, we remain dedicated to ensuring that PostSharp users can stay current with the evolving .NET platform.&lt;/p&gt;

&lt;p&gt;Happy meta-programming with PostSharp!&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/postsharp-2026-0-ga"&gt;PostSharp 2026.0 Generally Available: Support for .NET 10, C# 14 and Extension Blocks&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Wed, 07 Jan 2026 07:00:00 +0000</pubDate>
        
        <link>https://postsharp.net/blog/postsharp-2026-0-ga</link>
        <guid isPermaLink="true">https://postsharp.net/blog/postsharp-2026-0-ga</guid>
        
        
        <category>PostSharp</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2026/2026-01-announcement/postsharp.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>Metalama 2026.0 Generally Available: Full Support for C# 14 and .NET 10</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2026/2026-01-announcement/metalama.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;Metalama 2026.0 is now generally available! This major release brings full support for C# 14 (the most significant evolution of the C# language in years), along with .NET 10 SDK and Visual Studio 2026 compatibility. We’re also closing one of the last feature gaps with PostSharp: the ability to override event handler invocations. And you’re getting first-class support for tuple types.&lt;/p&gt;

&lt;p&gt;Ready to try it? The packages are on &lt;a href="https://www.nuget.org/packages/Metalama.Framework"&gt;NuGet.org&lt;/a&gt;, and the Visual Studio extension is available from the &lt;a href="https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp"&gt;Visual Studio Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the complete list of changes, see the &lt;a href="https://doc.metalama.net/conceptual/release-notes/release-notes-2026-0"&gt;release notes&lt;/a&gt;. For even more detail, see the change notes for the releases &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.11"&gt;2026.0.11&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.10-rc"&gt;2026.0.10-rc&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.9-rc"&gt;2026.0.9-rc&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.8-rc"&gt;2026.0.8-rc&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.7-rc"&gt;2026.0.7-rc&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.6-rc"&gt;2026.0.6-rc&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.5-preview"&gt;2026.0.5-preview&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.4-preview"&gt;2026.0.4-preview&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.3-preview"&gt;2026.0.3-preview&lt;/a&gt;, &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.2-preview"&gt;2026.0.2-preview&lt;/a&gt;,
  &lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.1-preview"&gt;2026.0.1-preview&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="c-14-support"&gt;C# 14 support&lt;/h2&gt;

&lt;p&gt;C# 14 introduces several language features, and Metalama 2026.0 supports all of them &lt;em&gt;in user code&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Extension blocks (see below)&lt;/li&gt;
  &lt;li&gt;Null-conditional assignments&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;field&lt;/code&gt; keyword in properties&lt;/li&gt;
  &lt;li&gt;Partial constructors and events&lt;/li&gt;
  &lt;li&gt;Compound assignment operators&lt;/li&gt;
  &lt;li&gt;Simple lambda parameters with modifiers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, some features are &lt;strong&gt;not yet supported in aspects&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Null-conditional assignments in templates&lt;/li&gt;
  &lt;li&gt;The &lt;code class="language-plaintext highlighter-rouge"&gt;field&lt;/code&gt; keyword in templates&lt;/li&gt;
  &lt;li&gt;Generating run-time code for extension members via invoker interfaces&lt;/li&gt;
  &lt;li&gt;Adding contracts to the receiver parameter of extension blocks&lt;/li&gt;
  &lt;li&gt;Introducing new compound assignment operators&lt;/li&gt;
  &lt;li&gt;Introducing parameters into partial constructors&lt;/li&gt;
  &lt;li&gt;Introducing new extension blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="extension-blocks"&gt;Extension blocks&lt;/h2&gt;

&lt;p&gt;Extension blocks are the headline feature of C# 14, letting you extend any type with new members. Metalama supports them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Extension blocks are modeled via the &lt;code class="language-plaintext highlighter-rouge"&gt;IExtensionBlock&lt;/code&gt; interface, which implements &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType&lt;/code&gt; (&lt;code class="language-plaintext highlighter-rouge"&gt;TypeKind = Extension&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;They appear under &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType.ExtensionBlocks&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;You can override extension members using the advising API or apply aspects via custom attributes.&lt;/li&gt;
  &lt;li&gt;You can introduce new members into existing extension blocks.&lt;/li&gt;
  &lt;li&gt;Introducing &lt;em&gt;new&lt;/em&gt; extension blocks is not yet supported.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how to add logging to all extension methods in your project using a fabric:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Fabric&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ProjectFabric&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AmendProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;IProjectAmender&lt;/span&gt; &lt;span class="n"&gt;amender&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;amender&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectTypes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExtensionBlocks&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAspectIfEligible&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LogAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="event-handler-overriding"&gt;Event handler overriding&lt;/h2&gt;

&lt;p&gt;You can now override event handler invocations, enabling patterns like “safe events” where each handler is isolated by exception handling. The &lt;code class="language-plaintext highlighter-rouge"&gt;OverrideInvoke&lt;/code&gt; method in &lt;code class="language-plaintext highlighter-rouge"&gt;OverrideEventAspect&lt;/code&gt; intercepts individual handler invocations.&lt;/p&gt;

&lt;p&gt;Typical use cases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Safe events (exception isolation)&lt;/li&gt;
  &lt;li&gt;Background or asynchronous event dispatch&lt;/li&gt;
  &lt;li&gt;Event invocation logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an aspect that catches exceptions and removes faulty handlers:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SafeEventAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OverrideEventAspect&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;OverrideInvoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Proceed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Apply it to any event:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Camera&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SafeEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;FocusChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnFocusChanged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FocusChanged&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For details, see &lt;a href="https://doc.metalama.net/conceptual/aspects/advising/overriding-events"&gt;Overriding events&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="first-class-tuple-support"&gt;First-class tuple support&lt;/h2&gt;

&lt;p&gt;Previously, Metalama treated tuples as plain &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType&lt;/code&gt; objects, with no access to element names or types. The new &lt;code class="language-plaintext highlighter-rouge"&gt;ITupleType&lt;/code&gt; interface changes that, making scenarios like packing method arguments for interceptors much cleaner:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a tuple type from method parameters.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tupleType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateTupleType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a tuple instance.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tupleExpression&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tupleType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateCreateInstanceExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Pass it to an observer.&lt;/span&gt;
&lt;span class="n"&gt;_myObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnMethodCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeclaringType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tupleExpression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="metrics-enhancements"&gt;Metrics enhancements&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;LinesOfCode metric&lt;/strong&gt;: Three measurements (Logical, NonBlank, and Total).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Workspaces API&lt;/strong&gt;: Now officially supports metrics consumption.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="visual-studio-tooling-improvements"&gt;Visual Studio tooling improvements&lt;/h2&gt;

&lt;p&gt;We’ve refactored several background components of our VSIX. It’s now more stable, consumes less CPU, and better utilizes your cores.&lt;/p&gt;

&lt;h2 id="claude-code-skill-for-ai-assisted-development"&gt;Claude Code skill for AI-assisted development&lt;/h2&gt;

&lt;p&gt;We’re introducing a Claude Code skill that gives Anthropic’s AI assistant Metalama knowledge:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Conceptual documentation&lt;/li&gt;
  &lt;li&gt;API references&lt;/li&gt;
  &lt;li&gt;Sample code and pattern libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you’re new to Metalama or tackling a complex scenario, Claude can now provide contextually relevant guidance for aspect development, templates, fabrics, and the code model.&lt;/p&gt;

&lt;p&gt;The skill is available on the &lt;a href="https://github.com/metalama/Metalama.AI.Skills"&gt;Metalama.AI.Skills marketplace&lt;/a&gt;. For setup instructions, see &lt;a href="https://doc.metalama.net/conceptual/using/claude-code"&gt;Configuring Claude Code&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="documentation-updates"&gt;Documentation updates&lt;/h2&gt;

&lt;p&gt;We’ve made these improvements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;New: &lt;a href="https://doc.metalama.net/conceptual/aspects/type-system"&gt;Working with types&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Improved: &lt;a href="https://doc.metalama.net/conceptual/aspects/templates"&gt;Writing T# templates&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Updated: &lt;a href="https://doc.metalama.net/conceptual/aspects/dependency-injection"&gt;Injecting dependencies into aspects&lt;/a&gt; (now covers cross-project DI)&lt;/li&gt;
  &lt;li&gt;Updated: &lt;a href="https://doc.metalama.net/conceptual/aspects/advising/overriding-events"&gt;Overriding events&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;New: &lt;a href="https://doc.metalama.net/conceptual/introspection/metrics"&gt;Consuming code metrics&lt;/a&gt; and &lt;a href="https://doc.metalama.net/conceptual/sdk/custom-metrics"&gt;Custom metrics&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Enhanced API docs with integrated conceptual content&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="breaking-changes"&gt;Breaking changes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;.NET 6 SDK is no longer supported.&lt;/li&gt;
  &lt;li&gt;Several API and behavioral changes. See the &lt;a href="https://doc.metalama.net/conceptual/release-notes/release-notes-2026-0"&gt;release notes&lt;/a&gt; for details.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="whats-next"&gt;What’s next&lt;/h2&gt;

&lt;p&gt;With 2026.0 shipped, we’ll focus on extending C# 14 support in aspects.&lt;/p&gt;

&lt;p&gt;Found an issue or have a feature request? Let us know on &lt;a href="https://github.com/metalama/Metalama/issues"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy meta-programming!&lt;/p&gt;

&lt;p&gt;-gael&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/metalama-2026-0-ga"&gt;Metalama 2026.0 Generally Available: Full Support for C# 14 and .NET 10&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Mon, 05 Jan 2026 07:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/metalama-2026-0-ga</link>
        <guid isPermaLink="true">https://postsharp.net/blog/metalama-2026-0-ga</guid>
        
        <category>featured</category>
        
        
        <category>Metalama</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2026/2026-01-announcement/metalama.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>Metalama November 2025 Update: Release Candidate for Metalama 2026.0</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2025/2025-11-status/november-dark.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;Metalama 2026.0 has reached Release Candidate status! All planned features are implemented, our test suite is green, and the release is stable enough for production evaluation. We’re now gathering community feedback ahead of our GA release on January 5th, 2026.&lt;/p&gt;

&lt;p&gt;Ready to try it? The RC packages are available on &lt;a href="https://www.nuget.org/packages/Metalama.Framework/2026.0.6-rc"&gt;NuGet.org&lt;/a&gt;, and the Visual Studio extension can be downloaded from the &lt;a href="https://www.postsharp.net/downloads/metalama/metalama-2026.0"&gt;Metalama 2026.0 downloads page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article covers what we shipped this month. For the complete list of changes in Metalama 2026.0, see the &lt;a href="https://doc.metalama.net/preview/conceptual/release-notes/release-notes-2026-0"&gt;release notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the full list of fixes and enhancements, see the changelogs:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.4-preview"&gt;2026.0.4-preview&lt;/a&gt; (November 8).&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.5-preview"&gt;2026.0.5-preview&lt;/a&gt; (November 12).&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.6-rc"&gt;2026.0.6-rc&lt;/a&gt; (November 28).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="c-14-extension-members"&gt;C# 14 Extension Members&lt;/h2&gt;

&lt;p&gt;This month, we completed support for C# 14 extension blocks. You can now advise extension members and introduce members into existing extension blocks.&lt;/p&gt;

&lt;p&gt;Extension blocks are the “extend everything” feature in C# 14. Here’s how Metalama models them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Extension blocks use the &lt;code class="language-plaintext highlighter-rouge"&gt;IExtensionBlock&lt;/code&gt; interface, which implements &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType&lt;/code&gt; (&lt;code class="language-plaintext highlighter-rouge"&gt;TypeKind = Extension&lt;/code&gt;) so you can discover members the usual &lt;code class="language-plaintext highlighter-rouge"&gt;Fields&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;Properties&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;Methods&lt;/code&gt;, … collections.&lt;/li&gt;
  &lt;li&gt;They appear under &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType.ExtensionBlocks&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Extension methods and property accessors are also listed in &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType.Methods&lt;/code&gt; as implicitly-implemented methods.&lt;/li&gt;
  &lt;li&gt;You can override extension members using the advising API or add aspects using custom attributes.&lt;/li&gt;
  &lt;li&gt;You can also add new extension members to existing extension blocks.&lt;/li&gt;
  &lt;li&gt;Introducing new extension blocks won’t be supported in 2026.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="other-improvements"&gt;Other Improvements&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single-file applications.&lt;/strong&gt; Metalama aspects now work in single-file &lt;code class="language-plaintext highlighter-rouge"&gt;dotnet run&lt;/code&gt; applications: arguably a niche scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Toast notifications.&lt;/strong&gt; Product briefs and news now appear via toast notifications on Windows. You can disable or configure this feature through the UI or CLI tool.&lt;/p&gt;

&lt;h2 id="visual-studio-tools-improved-stability-and-performance"&gt;Visual Studio Tools improved stability and performance&lt;/h2&gt;

&lt;p&gt;During the last two weeks, we focused heavily on Visual Studio extension stability, and uncovered more issues than expected.&lt;/p&gt;

&lt;p&gt;We owe you an apology. Most of these bugs were already present in the 2025.1 release, though some may have been introduced by a Visual Studio 17.x minor update (we don’t repeat our full manual test suite after each VS release, so we can’t be certain). We discovered them through exhaustive testing on clean virtual machines as part of our RC validation process.&lt;/p&gt;

&lt;p&gt;This level of testing clearly wasn’t done properly for 2025.1. To ensure quality meets our standards, we chose to delay the release by one month, missing our target to reach GA before the Christmas break. It was the right call.&lt;/p&gt;

&lt;p&gt;The good news: the Visual Studio extension is now significantly more stable, and we improved performance along the way. You can download it from the &lt;a href="https://www.postsharp.net/downloads/metalama/metalama-2026.0"&gt;Metalama 2026.0 downloads page&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="known-issues"&gt;Known Issues&lt;/h2&gt;

&lt;p&gt;A couple of items to address before GA:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There’s a known issue (&lt;a href="https://github.com/metalama/Metalama.Compiler/issues/149"&gt;#149&lt;/a&gt;) with the &lt;code class="language-plaintext highlighter-rouge"&gt;apt&lt;/code&gt; package on Linux. We’re working on a fix.&lt;/li&gt;
  &lt;li&gt;The next RC will update to Roslyn 5.0.0 GA (&lt;a href="https://github.com/metalama/Metalama/issues/1212"&gt;#1212&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="whats-next"&gt;What’s Next&lt;/h2&gt;

&lt;p&gt;With full .NET 10 SDK and C# 14 support (except for by-design limitations documented in the &lt;a href="https://doc.metalama.net/preview/conceptual/release-notes/release-notes-2026-0"&gt;release notes&lt;/a&gt;), and a significantly more stable Visual Studio extension, we’re on track for GA on January 5th, 2026.&lt;/p&gt;

&lt;p&gt;If you haven’t tried the RC yet, now is the time. Test it with your projects and let us know what you find. Your feedback helps us ship a stable release.&lt;/p&gt;

&lt;p&gt;Happy meta-programming!&lt;/p&gt;

&lt;p&gt;-gael&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/status-2025-11"&gt;Metalama November 2025 Update: Release Candidate for Metalama 2026.0&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Sun, 30 Nov 2025 07:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/status-2025-11</link>
        <guid isPermaLink="true">https://postsharp.net/blog/status-2025-11</guid>
        
        <category>featured</category>
        
        
        <category>Metalama</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2025/2025-11-status/november-dark.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>Metalama October 2025 Update: Full Steam Ahead Toward 2026.0</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2025/2025-10-status/october-dark.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;October is traditionally our busiest month. Microsoft ships the RCs of the .NET SDK, C# and Visual Studio, and we sprint to make Metalama ready. This year is no exception: we’ve made solid progress, and we’re confident we’ll be ready on time for the GA wave. Below is a recap of what we shipped and what’s coming.&lt;/p&gt;

&lt;h2 id="metalama-20252-skipped-for-simplicity"&gt;Metalama 2025.2: skipped for simplicity&lt;/h2&gt;

&lt;p&gt;We’ve decided not to ship Metalama 2025.2 as a standalone release. All its features, including the ability to override event handler invocations, are already merged into Metalama 2026.0. Why? The only real benefit of releasing 2025.2 would be making event-invocation advising available a few weeks earlier, but supporting two versions in parallel would slow us down.&lt;/p&gt;

&lt;h2 id="metalama-20260-almost-there"&gt;Metalama 2026.0: almost there&lt;/h2&gt;

&lt;p&gt;We are now almost code-complete with Metalama 2026.0, and have released several previews:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.3-preview"&gt;2026.0.3-preview changelog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.2-preview"&gt;2026.0.2-preview changelog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.1-preview"&gt;2026.0.1-preview changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="net-10-sdk-and-c-14"&gt;.NET 10 SDK and C# 14&lt;/h3&gt;

&lt;p&gt;We’re nearly done with support for the .NET 10 SDK and C# 14. Want to see where things stand? Check out these resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://github.com/orgs/metalama/projects/1/views/2?sliceBy%5Bvalue%5D=2026.0"&gt;General status board&lt;/a&gt; highlighting the leftovers for the 2026.0 version.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/orgs/metalama/projects/1/views/14"&gt;C# 14 status board&lt;/a&gt; showing our progress with C# 14 features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of scope for Metalama 2026.0:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Using null-conditional assignments in templates. &lt;a href="https://github.com/metalama/Metalama/issues/1109"&gt;#1109&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Using the &lt;code class="language-plaintext highlighter-rouge"&gt;field&lt;/code&gt; keyword in templates. &lt;a href="https://github.com/metalama/Metalama/issues/1114"&gt;#1114&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Introducing new extension blocks. &lt;a href="https://github.com/metalama/Metalama/issues/1159"&gt;#1159&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;del&gt;Introducing extension members into existing extension blocks. &lt;a href="https://github.com/metalama/Metalama/issues/1160"&gt;#1160&lt;/a&gt;&lt;/del&gt; UPDATE: implemented in 2026.0.4-preview.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fun fact&lt;/strong&gt;: we noticed that compiling our &lt;a href="https://github.com/metalama/Metalama.Tests.NopCommerce"&gt;NopCommerce snapshot&lt;/a&gt; with C# 14 breaks LINQ-to-SQL because of implicit conversions of arrays to &lt;code class="language-plaintext highlighter-rouge"&gt;ReadOnlySpan&lt;/code&gt;. We’ve had to pin this solution to C# 13 to work around this issue.&lt;/p&gt;

&lt;h3 id="extension-blocks"&gt;Extension blocks&lt;/h3&gt;

&lt;p&gt;“Extend everything” is the headline feature of C# 14, and we’re making sure Metalama supports it out of the box, including overriding extension members. Here’s how we’re modeling extension blocks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Extension blocks use the &lt;code class="language-plaintext highlighter-rouge"&gt;IExtensionBlock&lt;/code&gt; interface (&lt;code class="language-plaintext highlighter-rouge"&gt;TypeKind = Extension&lt;/code&gt;). It derives from &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType&lt;/code&gt;, and adds two properties: &lt;code class="language-plaintext highlighter-rouge"&gt;ReceiverParameter&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ReceiverType&lt;/code&gt;. Although extension blocks derive from &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType&lt;/code&gt;, you can’t always treat them like regular ones. Yes, this technically bends the Liskov Substitution Principle, but splitting the hierarchy would make the API noisier for little practical benefit. For extension members (except classic extension methods), &lt;code class="language-plaintext highlighter-rouge"&gt;IMember.DeclaringType&lt;/code&gt; points to the &lt;code class="language-plaintext highlighter-rouge"&gt;IExtensionBlock&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Extension blocks show up under &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType.ExtensionBlocks&lt;/code&gt;, not as nested types (&lt;code class="language-plaintext highlighter-rouge"&gt;INamedType.Types&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Extension methods and property accessors are listed in &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType.Methods&lt;/code&gt; as implicitly-implemented methods. They exist in IL and are addressable in C#, but you can’t override them with Metalama.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach feels natural for C# developers and closely matches Roslyn’s model.&lt;/p&gt;

&lt;h3 id="first-class-support-for-named-tuples"&gt;First-class support for named tuples&lt;/h3&gt;

&lt;p&gt;Previously, Metalama treated tuples as plain &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType&lt;/code&gt; objects, without access to element types or names. This made scenarios like packing method arguments for interceptors (think caching) awkward and inefficient. Now, with the new &lt;code class="language-plaintext highlighter-rouge"&gt;ITupleType&lt;/code&gt; interface, you get direct access to element names and types. Create named tuples with &lt;code class="language-plaintext highlighter-rouge"&gt;TypeFactory.CreateTupleType&lt;/code&gt;, and instantiate them with &lt;code class="language-plaintext highlighter-rouge"&gt;ITupleType.CreateCreateInstanceExpression&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our implementation even supports tuples with zero or one element. For two or more, you get the nice tuple syntax; for degenerate cases, Metalama falls back to &lt;code class="language-plaintext highlighter-rouge"&gt;ValueTuple.Create(...)&lt;/code&gt;. When you use &lt;code class="language-plaintext highlighter-rouge"&gt;ITupleType&lt;/code&gt;, you don’t have to worry about these details yourself.&lt;/p&gt;

&lt;p&gt;Example: Want to advise multiple methods and pass all arguments to an observer? Here’s how:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create the tuple type.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tupleType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateTupleType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create the tuple instance.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tupleExpression&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tupleType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateCreateInstanceExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Invoke the interceptor.&lt;/span&gt;
&lt;span class="n"&gt;_myObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnMethodCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeclaringType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tupleExpression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Transformed code:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Two or more arguments: tuple syntax&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Hat&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FindHats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_myObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnMethodCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HatStore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"FindHats"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// One argument: ValueTuple.Create&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsHatAvailable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;hatId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_myObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnMethodCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HatStore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"IsHatAvailable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValueTuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hatId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Zero arguments&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsStoreOpen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_myObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnMethodCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HatStore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"IsStoreOpen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValueTuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class="note"&gt;&lt;strong&gt;Breaking Change:&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;INamedType.TypeKind&lt;/code&gt; now returns &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;NamedType&lt;/code&gt; for tuples.&lt;/p&gt;

&lt;h3 id="advising-event-invocations"&gt;Advising event invocations&lt;/h3&gt;

&lt;p&gt;Overriding event handler invocation logic, originally planned for 2025.2, is now part of 2026.0. For details, see the &lt;a href="/blog/status-2025-09#metalama-20252-rc-event-invocation-overrides"&gt;September 2025 Status Update&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="other-improvements"&gt;Other improvements&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Introducing user-defined &lt;code class="language-plaintext highlighter-rouge"&gt;checked&lt;/code&gt; operators.&lt;/li&gt;
  &lt;li&gt;Dependency injection: pull constructor parameters across projects.&lt;/li&gt;
  &lt;li&gt;API cleanup (some breaking changes, &lt;code class="language-plaintext highlighter-rouge"&gt;[Obsolete]&lt;/code&gt; warnings).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="metalama-20251-now-supports-visual-studio-2026"&gt;Metalama 2025.1: now supports Visual Studio 2026&lt;/h2&gt;

&lt;p&gt;Last month, we added limited .NET 10 SDK support to Metalama 2025.1. As of October 7, Visual Studio 2026 is also supported.&lt;/p&gt;

&lt;p&gt;Just make sure to update both the NuGet packages and the VSIX extension.&lt;/p&gt;

&lt;h2 id="roadmap-finalizing-20260"&gt;Roadmap: finalizing 2026.0&lt;/h2&gt;

&lt;p&gt;We’re wrapping up the last features for full C# 14 support in Metalama 2026.0. Next up: stabilization, code cleanup, manual testing, and performance validation.&lt;/p&gt;

&lt;p&gt;We’re aiming for a release candidate (RC) on November 7, barring any surprises during testing. The final GA release will follow soon after Microsoft ships the GA versions of the .NET SDK and related packages.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In October, we made good progress on Metalama 2026.0, whose main focus is to support the .NET 10 SDK, C# 14, and Visual Studio 2026. We’re almost code complete, and we’ll spend most of November stabilizing the release.&lt;/p&gt;

&lt;p&gt;Happy meta-programming!&lt;/p&gt;

&lt;p&gt;-gael&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/status-2025-10"&gt;Metalama October 2025 Update: Full Steam Ahead Toward 2026.0&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Thu, 30 Oct 2025 07:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/status-2025-10</link>
        <guid isPermaLink="true">https://postsharp.net/blog/status-2025-10</guid>
        
        <category>featured</category>
        
        
        <category>Metalama</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2025/2025-10-status/october-dark.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>Metalama Status Update, September 2025</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2025/2025-09-status/september-dark.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;Welcome to Metalama’s September 2025 status update. After a relaxed summer, we’re back in full swing, delivering .NET 10 SDK support for Metalama and closing a key gap versus PostSharp: the ability to override event handler invocations.&lt;/p&gt;

&lt;h2 id="our-first-community-contributor"&gt;Our first community contributor&lt;/h2&gt;

&lt;p&gt;&lt;img src="/assets/images/2025/2025-09-status/beppemarazzi.jpeg" alt="Giuseppe Marazzi" class="portrait" /&gt;&lt;/p&gt;

&lt;p&gt;Let me start with the announcement that brings me the greatest joy.&lt;/p&gt;

&lt;p&gt;This past summer, Metalama received its first contribution from the open-source community. &lt;a href="https://github.com/beppemarazzi"&gt;Giuseppe Marazzi&lt;/a&gt; submitted &lt;a href="https://github.com/metalama/Metalama/pulls?q=author%3Abeppemarazzi++is%3Apr&amp;amp;filterScopeGroup=true"&gt;two PRs&lt;/a&gt; fixing two small and hard-to-find bugs.&lt;/p&gt;

&lt;p&gt;A warm thanks to Giuseppe!&lt;/p&gt;

&lt;p&gt;While Metalama may look like a complex codebase at first glance, Giuseppe’s contribution proves that anyone can make a meaningful impact. Don’t let the technical depth intimidate you: every contribution counts, however small. Our &lt;a href="https://doc.metalama.net/contributing"&gt;contributor’s guide&lt;/a&gt; shows you where to start.&lt;/p&gt;

&lt;h2 id="net-sdk-10-readiness"&gt;.NET SDK 10 readiness&lt;/h2&gt;

&lt;p&gt;As autumn arrives, Microsoft is finalizing the new .NET stack, and, like all .NET extenders, we’re working hard to make sure you can use the new features as soon as possible.&lt;/p&gt;

&lt;p&gt;We’ve split the work into two releases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Metalama 2025.1 received quick fixes to keep working with the .NET 10 SDK, with limitations;&lt;/li&gt;
  &lt;li&gt;Metalama 2026.0 will be built natively for the .NET 10 SDK.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="metalama-20251-limited-net-sdk-10-support"&gt;Metalama 2025.1: limited .NET SDK 10 support&lt;/h3&gt;

&lt;p&gt;With the .NET SDK 10 already in RC, we didn’t want you to wait for full integration, so we made small changes to ensure compatibility with the new .NET SDK.&lt;/p&gt;

&lt;p&gt;This build is Metalama 2025.1.14, released this week. From our &lt;a href="https://github.com/metalama/Metalama.Tests.DotNetSdk/actions/workflows/test.yml"&gt;platform integration matrix tests&lt;/a&gt;, most .NET workloads are working.&lt;/p&gt;

&lt;p&gt;Keep in mind that Metalama 2025.1 is still based on Roslyn 4.14, while the .NET SDK 10 includes Roslyn 5.0. This has some implications:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Analyzers and source generators compiled against Roslyn 5.0 will not work. When possible, we replace them with an older Roslyn 4.14 version. This often works, but not always.&lt;/li&gt;
  &lt;li&gt;Specifically, Blazor projects (all flavors) are not supported yet because of an incompatibility in their source generators.&lt;/li&gt;
  &lt;li&gt;You cannot use C# 14 features in your Metalama projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="metalama-20260-in-progress"&gt;Metalama 2026.0 in progress&lt;/h3&gt;

&lt;p&gt;The goal of Metalama 2026.0 is to bring full native support for the .NET 10 SDK and C# 14: native, not hacks.&lt;/p&gt;

&lt;p&gt;C# 14 brings a lot of new features, and we won’t support all of them in every scenario in November, when the new .NET is expected to become generally available. Our primary goal with Metalama 2026.0 is to let you safely use any new C# feature in your runtime code, but not necessarily in aspect templates. Support for new language features in templates may come in later versions. This aligns with our design philosophy: Metalama should be able to compile any valid C# code, but it’s okay to limit features available in aspects.&lt;/p&gt;

&lt;p&gt;We’re using GitHub to track our progress. You can use &lt;a href="https://github.com/metalama/Metalama/issues/1045"&gt;this meta-issue&lt;/a&gt; or &lt;a href="https://github.com/orgs/metalama/projects/1/views/2?sliceBy%5Bvalue%5D=2026.0"&gt;this dashboard&lt;/a&gt; to check the status.&lt;/p&gt;

&lt;p&gt;We plan to have an RC ready in November, when Microsoft is expected to release the new stack.&lt;/p&gt;

&lt;p&gt;If you’re eager for a preview, look for one next week. Or, if you’re more patient, compile the develop/2026.0 branch of &lt;a href="https://github.com/metalama/Metalama/tree/develop/2026.0"&gt;Metalama&lt;/a&gt; and &lt;a href="https://github.com/metalama/Metalama.Compiler/tree/develop/2026.0"&gt;Metalama.Compiler&lt;/a&gt; repos (merged from Roslyn tag &lt;code class="language-plaintext highlighter-rouge"&gt;NET-SDK-10.0.100-preview.7.25380.108&lt;/code&gt; as of today).&lt;/p&gt;

&lt;h2 id="visual-studio-2026-readiness"&gt;Visual Studio 2026 readiness&lt;/h2&gt;

&lt;p&gt;We ran some initial tests of our Metalama Tools for Visual Studio with Visual Studio 2026. They failed, subtly but miserably.&lt;/p&gt;

&lt;p&gt;It seems those incompatibilities can be addressed with fairly small bug fixes. If that proves to be true, we’ll update our tooling 2025.1 to support Visual Studio 2026, and there will be no need to wait for Metalama 2026.0.&lt;/p&gt;

&lt;h2 id="metalama-20252-rc-event-invocation-overrides"&gt;Metalama 2025.2 RC: Event invocation overrides&lt;/h2&gt;

&lt;p&gt;We have just released the first (and hopefully final) RC of Metalama 2025.2.&lt;/p&gt;

&lt;p&gt;Since the beginning, Metalama has allowed overriding the &lt;em&gt;add&lt;/em&gt; and &lt;em&gt;remove&lt;/em&gt; accessors of events, which were arguably of limited utility. This version adds the ability to override the &lt;em&gt;invocation&lt;/em&gt; of event handlers. The OverrideInvoke method of the OverrideEventAspect class lets you intercept the invocation of an individual event handler.&lt;/p&gt;

&lt;p&gt;Typical use cases are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;safe events (exception handling)&lt;/li&gt;
  &lt;li&gt;background/asynchronous events&lt;/li&gt;
  &lt;li&gt;event invocation logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s illustrate this with an example. This aspect catches exceptions in event handlers; if any exception is caught, it removes it from the event invocation list.&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SafeEventAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OverrideEventAspect&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If there are many event handlers, this method will be called several times.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;OverrideInvoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Proceed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// First time shame on you, second time shame on me.&lt;/span&gt;
            &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now add this to any event:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Camera&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SafeEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;FocusChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnFocusChanged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FocusChanged&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you might expect, it took us a while to implement. It’s far from simple.&lt;/p&gt;

&lt;p&gt;Here’s how Metalama transforms your code:&lt;/p&gt;

&lt;div class="language-csharp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Camera&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The field backing the event.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_focusChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// A broker sitting between the event API and the backing field.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="n"&gt;ActionEventBroker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="p"&gt;)&amp;gt;?&lt;/span&gt; &lt;span class="n"&gt;_focusChangedBroker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Binds the broker to the event implementation and the advice.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ActionEventBrokerCallbacks&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="p"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FocusChangedBrokerCallbacks_0&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;FocusChanged_Invoke_SafeEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;FocusChanged_SafeEvent&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;FocusChanged_SafeEvent&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;



    &lt;span class="c1"&gt;// The public event with the new implementation.&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SafeEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;FocusChanged&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;add&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ActionEventBroker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="p"&gt;)&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnsureInitialized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_focusChangedBroker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FocusChangedBrokerCallbacks_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_focusChangedBroker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;remove&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_focusChangedBroker&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;RemoveHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// The original event implementation.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;FocusChanged_SafeEvent&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;add&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_focusChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;remove&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_focusChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Your advice intercepting the handler invocation.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FocusChanged_Invoke_SafeEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;_focusChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnFocusChanged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_focusChanged&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s a bit lengthy, but aside from a touch of syntactic sugar, the event broker is the most reliable pattern, because we can’t predict or analyze how an event is raised. This approach is oblivious to the event’s implementation and to how it gets raised.&lt;/p&gt;

&lt;p&gt;You can find more details on this &lt;a href="https://doc.metalama.net/preview/conceptual/aspects/advising/overriding-events"&gt;documentation page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Subscribe to our &lt;a href="/metalama/briefs"&gt;briefs&lt;/a&gt; to receive updates about ongoing work on Metalama 2026.0 before the next monthly update.&lt;/p&gt;

&lt;h2 id="dockerization-of-our-build-infrastructure"&gt;Dockerization of our build infrastructure&lt;/h2&gt;

&lt;p&gt;As a side note, the entire Metalama build infrastructure now runs on Docker, requiring only lean build agents.&lt;/p&gt;

&lt;p&gt;That means you can build Metalama in the exact environment we use.&lt;/p&gt;

&lt;p&gt;All you need is a Windows 11 or Windows Server 2025 machine with Docker installed and Windows containers enabled, then run:&lt;/p&gt;

&lt;div class="language-powershell highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\DockerBuild.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;

&lt;p&gt;On the feature side, the &lt;code class="language-plaintext highlighter-rouge"&gt;OverrideEventAspect.OverrideInvoke&lt;/code&gt; advice closes one of Metalama’s remaining gaps with PostSharp. On the platform front, we’ve released the first bits of .NET 10 support and we expect to deliver more soon. In terms of infrastructure, Dockerizing our build workflow makes Metalama easy to reproduce across agents and helps reduce the cost and complexity of our setup.&lt;/p&gt;

&lt;p&gt;Happy meta-programming with Metalama!&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/status-2025-09"&gt;Metalama Status Update, September 2025&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Wed, 01 Oct 2025 07:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/status-2025-09</link>
        <guid isPermaLink="true">https://postsharp.net/blog/status-2025-09</guid>
        
        <category>featured</category>
        
        
        <category>Metalama</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2025/2025-09-status/september-dark.svg</url>
          
        </image>
      </item>
    
      <item>
        <title>Security Notice: Data Leak Affecting a Limited Number of Accounts</title>
        <description>
           
          &lt;p&gt;We regret to inform you of a data leak that occurred on the &lt;strong&gt;PostSharp Customer Portal&lt;/strong&gt; between &lt;strong&gt;Wednesday, September 3rd, and Tuesday, September 9th&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id="who-is-affected"&gt;Who is affected?&lt;/h3&gt;

&lt;p&gt;A total of &lt;strong&gt;38 customer accounts&lt;/strong&gt; were impacted. If your account was affected, you will:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Receive a personal notification email within the next 24 hours.&lt;/li&gt;
  &lt;li&gt;Notice that you can no longer sign in to the portal using your license keys.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="what-data-was-leaked"&gt;What data was leaked?&lt;/h3&gt;

&lt;p&gt;Only the content of the &lt;a href="https://www.postsharp.net/account"&gt;https://www.postsharp.net/account&lt;/a&gt; page of the portal was exposed, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Company name and billing address (or customer name and address for natural persons)&lt;/li&gt;
  &lt;li&gt;All subscription dates, number of users, licensed products, and support level&lt;/li&gt;
  &lt;li&gt;All license keys for PostSharp and Metalama&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="what-was-not-leaked"&gt;What was &lt;em&gt;not&lt;/em&gt; leaked?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Contact lists or email addresses&lt;/li&gt;
  &lt;li&gt;Prices, invoices, or quotes&lt;/li&gt;
  &lt;li&gt;Support tickets&lt;/li&gt;
  &lt;li&gt;Source code (never uploaded to our servers)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="who-accessed-the-data"&gt;Who accessed the data?&lt;/h3&gt;

&lt;p&gt;At most, &lt;strong&gt;one other PostSharp customer&lt;/strong&gt; may have received your data in error.&lt;/p&gt;

&lt;h3 id="how-did-this-happen"&gt;How did this happen?&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;caching error&lt;/strong&gt; occurred in the daily job that sends subscription renewal notifications, causing account data to be sent to the wrong recipient. The first customer of the batch received all emails.&lt;/p&gt;

&lt;h3 id="timeline-of-events"&gt;Timeline of events&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Wed 9/3, 11:00 AM&lt;/strong&gt; – First incorrect email sent.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mon 9/8, 8:00 PM&lt;/strong&gt; – Data leak reported by the receiving customer.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tue 9/9, 2:00 PM&lt;/strong&gt; – Report read and incident confirmed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tue 9/9, 4:30 PM&lt;/strong&gt; – Credentials invalidated and issue remediated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="how-we-fixed-it"&gt;How we fixed it?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;All past &lt;strong&gt;magic URLs&lt;/strong&gt; were invalidated by rotating the salt for each affected account.&lt;/li&gt;
  &lt;li&gt;License keys exposed in the leak were &lt;strong&gt;disabled for authentication&lt;/strong&gt; to the portal.&lt;/li&gt;
  &lt;li&gt;The caching bug was fixed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="do-you-need-to-take-action"&gt;Do you need to take action?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No action is required&lt;/strong&gt;. Your license keys will continue to work normally with PostSharp and Metalama products.&lt;/li&gt;
  &lt;li&gt;If you prefer, we can issue &lt;strong&gt;new license keys&lt;/strong&gt; on request, to ensure that any leaked key cannot be used by another party.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="how-to-log-in-now"&gt;How to log in now?&lt;/h3&gt;

&lt;p&gt;If your license keys no longer work for portal login:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use &lt;strong&gt;email authentication&lt;/strong&gt; (one-time PIN or magic link) for one of your account contacts.&lt;/li&gt;
  &lt;li&gt;If you do not know your contact, please write to &lt;strong&gt;&lt;a href="mailto:hello@postsharp.net"&gt;hello@postsharp.net&lt;/a&gt;&lt;/strong&gt; from your work email.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We take full responsibility for this incident and sincerely apologize for the error. We are committed to ensuring it does not happen again.&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/2025-09-09-security-notice"&gt;Security Notice: Data Leak Affecting a Limited Number of Accounts&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Sun, 07 Sep 2025 23:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/2025-09-09-security-notice</link>
        <guid isPermaLink="true">https://postsharp.net/blog/2025-09-09-security-notice</guid>
        
        
        <category>PostSharp</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/blog/ps-no-featimg-16x9.jpg</url>
          
        </image>
      </item>
    
      <item>
        <title>Breaking Update of PostSharp Caching adapter for Redis in 2025.1</title>
        <description>
           
          &lt;img src="https://postsharp.net/assets/images/2025/2025-09-redis-caching/caching.svg" style="width: 100%"/&gt;
          
          &lt;p&gt;We have released a refactored version of &lt;code class="language-plaintext highlighter-rouge"&gt;PostSharp.Patterns.Caching.Backends.Redis&lt;/code&gt; in PostSharp 2025.1 to address a reliability issue that could surface in multi-node Redis deployments (master/replica or cluster) when cache dependencies are enabled under sustained load. The fix required a redesign of the internal data schema, which is why this is a breaking change (one-time cache purge). Outside of that purge, the upgrade is straightforward and brings measurable gains in performance, consistency, and resilience.&lt;/p&gt;

&lt;p&gt;Impact at a glance:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If you run multi-node Redis with dependencies, the update removes a potential failure mode.&lt;/li&gt;
  &lt;li&gt;You only need a one-time cache purge, and you’ll still benefit from performance and diagnostics improvements.&lt;/li&gt;
  &lt;li&gt;There is no risk of user data loss; only old cached entries stay in the database until purged.&lt;/li&gt;
  &lt;li&gt;No impact for the other backends.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="who-is-affected"&gt;Who is affected?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Affected by the bug:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Applications that use &lt;code class="language-plaintext highlighter-rouge"&gt;PostSharp.Patterns.Caching.Backends.Redis&lt;/code&gt; with support for dependencies in a Redis master/replica or cluster setup.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Affected by the refactoring:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;All applications that use &lt;code class="language-plaintext highlighter-rouge"&gt;PostSharp.Patterns.Caching.Backends.Redis&lt;/code&gt;, with or without support for dependencies, are affected by the redesign of the data schema. You must purge the cache after the update, and there is no other impact. All versions prior to 2025.1.7 are affected.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Customers using &lt;code class="language-plaintext highlighter-rouge"&gt;Metalama.Patterns.Caching.Backends.Redis&lt;/code&gt; are affected by this issue too, and the solution has not yet been ported to Metalama. Please contact us if it matters to you.&lt;/p&gt;

&lt;h2 id="what-was-wrong"&gt;What was wrong?&lt;/h2&gt;

&lt;p&gt;The previous design of the &lt;code class="language-plaintext highlighter-rouge"&gt;RedisCachingBackend&lt;/code&gt; relied on a retry loop in &lt;code class="language-plaintext highlighter-rouge"&gt;GetItem&lt;/code&gt; operations to retrieve a consistent snapshot of the cache item, which includes both the cache value and its dependencies, stored in separate Redis keys. Under heavy load in a master/replica setup, this loop could exhaust its iterations and fail.&lt;/p&gt;

&lt;p&gt;The consistent snapshot of value and dependencies was required because recursive dependencies (when a cached method calls another already cached method) were flattened in the &lt;code class="language-plaintext highlighter-rouge"&gt;SetItem&lt;/code&gt; operation, so the &lt;code class="language-plaintext highlighter-rouge"&gt;RemoveItem&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;InvalidateItem&lt;/code&gt; operations did not need to operate recursively. This was a poor design choice.&lt;/p&gt;

&lt;p&gt;The previous component was only tested in a single-node Redis topology. We believe the root cause was a lack of testing in multi-node topologies under high load.&lt;/p&gt;

&lt;h2 id="how-was-this-fixed"&gt;How was this fixed?&lt;/h2&gt;

&lt;p&gt;We redesigned the data schema so &lt;code class="language-plaintext highlighter-rouge"&gt;GetItem&lt;/code&gt; never needs to iterate:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Dependencies are no longer flattened. The &lt;code class="language-plaintext highlighter-rouge"&gt;RemoveItem&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;InvalidateItem&lt;/code&gt; operations now work recursively.&lt;/li&gt;
  &lt;li&gt;Cache keys (a single logical cache item requires several Redis keys) are now versioned, so we don’t need loops to ensure read consistency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="what-else-has-been-improved"&gt;What else has been improved?&lt;/h2&gt;

&lt;p&gt;Since a major refactoring was required to implement the new data schema, we went the extra mile and made the component more resilient in high-load production environments:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Performance improvements&lt;/strong&gt; due to the new design of the Redis data schema that is always observationally consistent for read operations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Key compression (hashing).&lt;/strong&gt; Enable this feature by assigning the &lt;code class="language-plaintext highlighter-rouge"&gt;CachingServices.DefaultKeyBuilder&lt;/code&gt; property.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;More resilient&lt;/strong&gt; thanks to new behaviors controlled by the following properties on the &lt;code class="language-plaintext highlighter-rouge"&gt;RedisCachingBackendConfiguration&lt;/code&gt; object:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Exception handling:&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;RedisCachingBackendConfiguration.ExceptionHandlingPolicy&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Retry policies:&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;RedisCachingBackendConfiguration.BackgroundRecoveryRetryPolicy&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;RedisCachingBackendConfiguration.BackgroundTasksRetryPolicy&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;RedisCachingBackendConfiguration.TransactionRetryPolicy&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Concurrency control:&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;BackgroundTasksMaxConcurrency&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;InvalidationMaxConcurrency&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Node selection:&lt;/strong&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;ReadCommandFlags&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;WriteCommandFlags&lt;/code&gt;, defaulting to &lt;code class="language-plaintext highlighter-rouge"&gt;PreferReplica&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;PreferMaster&lt;/code&gt;, respectively.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Support for sharding clusters,&lt;/strong&gt; with keys containing taghashes. Tested on a 3+3 cluster.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Support for dependencies&lt;/strong&gt; with a very large number of dependent cache items.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GC process:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Detection of overloads and automatic temporary disabling.&lt;/li&gt;
      &lt;li&gt;Periodic background cleanups to handle situations that were not detected by processing keyspace notifications in real time.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keys contain the PostSharp Caching schema version&lt;/strong&gt; for safe updates.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed bugs&lt;/strong&gt; with sliding expiration.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improved &lt;a href="https://doc.postsharp.net/caching/backends/caching-redis"&gt;documentation&lt;/a&gt;.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="what-changes-are-breaking"&gt;What changes are breaking?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The data schema has changed, so you must purge the old version’s cache; otherwise it will contain permanently unreachable data (garbage) forever.&lt;/li&gt;
  &lt;li&gt;The &lt;code class="language-plaintext highlighter-rouge"&gt;CacheValue.Dependencies&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;CacheItem.Dependencies&lt;/code&gt; properties now contain first-level dependencies rather than all recursive dependencies. These objects are largely implementation details and do not surface to the aspect-oriented API of PostSharp Caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="how-to-update"&gt;How to update?&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Update all PostSharp packages to &lt;code class="language-plaintext highlighter-rouge"&gt;2025.1.8&lt;/code&gt; and deploy your application.&lt;/li&gt;
  &lt;li&gt;Purge the cache using Redis’s &lt;code class="language-plaintext highlighter-rouge"&gt;FLUSHDB&lt;/code&gt; command after your old application has been undeployed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you ignore this post and only update the package, the Redis database will contain the data of the old version forever, since the dependency-management keys are set to never expire.&lt;/p&gt;

&lt;h2 id="read-more"&gt;Read more&lt;/h2&gt;

&lt;p&gt;For details about the refactored PostSharp Caching for Redis, see our &lt;a href="https://doc.postsharp.net/caching/backends/caching-redis"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;PostSharp Caching for Redis now runs on a more robust, versioned schema that removes a reliability edge case and improves performance and consistency across single-node, replicated, and sharded clusters when dependency support is enabled. The component has been overhauled for better observability and resilience. The only required action is a one-time cache purge after upgrading to 2025.1.8 or later.&lt;/p&gt;

&lt;p&gt;If you rely on the Metalama Redis backend, let us know so we can prioritize porting the same improvements.&lt;/p&gt;

&lt;p&gt;Thanks to customers who surfaced the scenario early—enjoy the improved backend.&lt;/p&gt;


          &lt;p&gt;
            This article was first published on a &lt;a href="https://postsharp.net/blog"&gt;https://postsharp.net/blog&lt;/a&gt;
            under the title &lt;a href="https://postsharp.net/blog/redis-caching-update"&gt;Breaking Update of PostSharp Caching adapter for Redis in 2025.1&lt;/a&gt;.
          &lt;/p&gt;
        </description>
        <pubDate>Mon, 01 Sep 2025 23:00:01 +0000</pubDate>
        
        <link>https://postsharp.net/blog/redis-caching-update</link>
        <guid isPermaLink="true">https://postsharp.net/blog/redis-caching-update</guid>
        
        
        <category>PostSharp</category>
        
        <image>
          
          <url>https://postsharp.net/assets/images/2025/2025-09-redis-caching/caching.svg</url>
          
        </image>
      </item>
    
  </channel>
</rss>