Attributes on lambda methods

Applying method attributes to lambda methods

Home DailyDrop

Daily Knowledge Drop

Introduced with C#10, attributes can also be applied to lambda expressions and lambda parameters.


Attribute

For our example, a method level attribute has been created, which can be used to mark a method for logging (this is just an example, no actual logging will be demonstrated in this post):

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
class LoggingEnabledAttribute : Attribute
{
    // marker attribute to flag this method should be logged
}

Attribute application

This attribute, configured to target methods (using another attribute, AttributeUsage) can now be applied to any method:

public class AttributeDemo
{
    // Method1 has been flagged for logging
    [LoggingEnabled]
    public void Method1()
    {
        Console.WriteLine("In AttributeDemo => Method1");
    }

    public void ActionMethod(Action action)
    {
        action.Invoke();
    }
}

In the above, Method1 has had the attribute applied to it.

Lambda attribute application

The AttributeDemo class above also has a method called ActionMethod, which takes in an Action. This Action can be represented by a lambda:

var ad = new AttributeDemo();

ad.ActionMethod(() => Console.WriteLine("In AttributeDemo => ActionMethod => action"));

The LoggingEnabledAttribute can also now be applied to the lambda:

var ad = new AttributeDemo();

ad.ActionMethod(() => Console.WriteLine("In AttributeDemo => ActionMethod => no logging"));

// Logging enabled!
ad.ActionMethod([LoggingEnabled]() => 
    Console.WriteLine("In AttributeDemo => ActionMethod => with logging"));

Relevant attributes which target parameters can also be applied to the lambda parameters.

Attribute confusion

To be able to use attributes on a lambda, some very minor code updates might be required. These are needed to indicate to the compiler what the attribute should be targeting:

// lambda takes no parameters
Action lambda1 = () => { };  

// lambda takes 1 parameter, parenthesis excluded
Action<int> lambda2 = x => x++;

// lambda takes 1 parameter, parenthesis included
Action<int> lambda3 = (x) => x++;

The the case of the second example in the above snippet, the lambda with the parenthesis excluded, if the attribute was applied, the compiler is unable to determine if the attribute should be applied to the method, or to the parameter:

// lambda takes no parameters
// Attribute applied without issue to the method
Action lambda1 = [LoggingEnabled]() => { };  

// lambda takes 1 parameter, parenthesis excluded
// NOT VALID - does the attribute apply to the method, or to the parameter x?
Action<int> lambda2 = [LoggingEnabled]x => x++;

// lambda takes 1 parameter, parenthesis included
// VALID - attribute applies to the method
Action<int> lambda3 = [LoggingEnabled](x) => x++;

// lambda takes 1 parameter, parenthesis included
// VALID - attribute LoggingEnabled applies to the method
// VALID - attribute AttributeForParameter applies to the method
Action<int> lambda4 = [LoggingEnabled]([AttributeForParameter]x) => x++;

If the code is using the style without the parenthesis, then to be able to use attributes, it would need to update to include the parenthesis.


Notes

A small, but useful improvement to C#, especially if the application make extensive use of attributes and/or lambdas.


References

Lambda Improvements: Attributes

Daily Drop 64: 02-05-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net attribute lambda