Skip to content
Igor Tkachev edited this page Dec 7, 2023 · 27 revisions

How it works

Lets create a new project and see step by step how it works.

Create a new project

AspectTest.csproj:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>
</Project>

Program.cs:

A.InterceptableMethod();

static class A
{
    public static void InterceptableMethod()
    {
        Console.WriteLine("interceptable");
    }
}

Run it and see output:

interceptable

Use InterceptsLocation attribute

Now lets add InterceptsLocation attribute to our code.

Modified project file:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>

        <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Interceptors</InterceptorsPreviewNamespaces>
    </PropertyGroup>
</Project>

Program.cs:

A.InterceptableMethod();

static class A
{
    public static void InterceptableMethod()
    {
        Console.WriteLine("interceptable");
    }
}

namespace Interceptors
{
    using System.Runtime.CompilerServices;

    class B
    {
        [InterceptsLocation(@"P:\Test\AspectTest\Program.cs", line: 1, character: 3)]
        public static void InterceptorMethod()
        {
            Console.WriteLine("interceptor");
        }
    }
}

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public sealed class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
    {
    }
}

Now we see different output:

interceptor

Note

When compiler sees InterceptsLocation attribute, it will replace call to InterceptableMethod with call to InterceptorMethod.

Use AspectGenerator

Modified project file:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>

        <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);AspectGenerator</InterceptorsPreviewNamespaces>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="AspectGenerator" Version="0.0.5-preview" />
    </ItemGroup>
</Project>

Program.cs:

A.InterceptableMethod();

static class A
{
    [Aspects.Intercept]
    public static void InterceptableMethod()
    {
        Console.WriteLine("interceptable");
    }
}

namespace Aspects
{
    using AspectGenerator;

    [Aspect(
        OnBeforeCall = nameof(OnBeforeCall)
        )]
    class InterceptAttribute : Attribute
    {
        public static void OnBeforeCall(InterceptInfo info)
        {
            Console.WriteLine("aspect");
            info.InterceptResult = InterceptResult.Return;
        }
    }
}

Now we see different output:

aspect

Interceptors.g.cs:

// <auto-generated/>
#pragma warning disable
#nullable enable

using System;

using SR  = System.Reflection;
using SLE = System.Linq.Expressions;
using SCG = System.Collections.Generic;

namespace AspectGenerator
{
    using AspectGenerator = AspectGenerator;

    static partial class Interceptors
    {
        static SR.MethodInfo GetMethodInfo(SLE.Expression expr)
        {
            return expr switch
            {
                SLE.MethodCallExpression mc => mc.Method,
                _                           => throw new InvalidOperationException()
            };
        }

        static SR.MethodInfo MethodOf<T>(SLE.Expression<Func<T>> func) => GetMethodInfo(func.Body);
        static SR.MethodInfo MethodOf   (SLE.Expression<Action>  func) => GetMethodInfo(func.Body);

        static SR. MemberInfo                 InterceptableMethod_Interceptor_MemberInfo        = MethodOf(() => A.InterceptableMethod());
        static SCG.Dictionary<string,object?> InterceptableMethod_Interceptor_AspectArguments_0 = new()
        {
        };
        //
        /// <summary>
        /// Intercepts A.InterceptableMethod().
        /// </summary>
        //
        // Intercepts A.InterceptableMethod().
        [System.Runtime.CompilerServices.InterceptsLocation(@"P:\Test\AspectTest\Program.cs", line: 1, character: 3)]
        //
        [System.Runtime.CompilerServices.CompilerGenerated]
        //[System.Diagnostics.DebuggerStepThrough]
        public static void InterceptableMethod_Interceptor()
        {
            // Aspects.InterceptAttribute
            //
            var __info__0 = new AspectGenerator.InterceptInfo<AspectGenerator.Void>
            {
                MemberInfo      = InterceptableMethod_Interceptor_MemberInfo,
                AspectType      = typeof(Aspects.InterceptAttribute),
                AspectArguments = InterceptableMethod_Interceptor_AspectArguments_0,
            };

            __info__0.InterceptType = AspectGenerator.InterceptType.OnBeforeCall;
            Aspects.InterceptAttribute.OnBeforeCall(__info__0);

            if (__info__0.InterceptResult != AspectGenerator.InterceptResult.Return)
            {
                A.InterceptableMethod();
            }
        }
    }
}
Clone this wiki locally