-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Igor Tkachev edited this page Dec 7, 2023
·
27 revisions
Lets create a new project and see step by step how it works.
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
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.
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();
}
}
}
}