Skip to content

This is a plugin for static weaving code that integrates MethodBoundaryAspect.Fody into a Unity project using the Loxodon Framework!

License

Notifications You must be signed in to change notification settings

finerace/MethodBoundaryAspect.Fody-for-Unity

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

31 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

icon Русский Русская вСрсия

logo

Write clean, boilerplate-free C# code with Aspect-Oriented Programming.

GitHub Stars Latest Release License Unity Version Status


TL;DR

  • ✨ AOP (Aspect-Oriented Programming) for Unity 2021.3+ with full UniTask/async support.
  • πŸš€ Eliminates boilerplate: logging, profiling, error handling, etc. β†’ up to βˆ’60% less repetitive code and more.
  • ⚑️ Zero runtime overhead β€” all code is weaved at compile time via IL.
  • πŸ› οΈ Extensible: easily create your own aspects (caching, validation, authorization, etc.).
  • πŸ“Š Speeds up development by ~20–30% in projects with heavy async usage.
  • πŸ”’ Stable performance: tested with async lambdas, compiler state machines, and IL2CPP builds.
  • πŸ›‘οΈ Reliable: The plugin has been tested in 40+ unique use cases across different Unity versions.
  • 🧩 Easily integrates into existing Unity projects and Zenject-based architectures.

🎯 The Problem: Repetitive, Scattered Code

Unity development is full of repetitive tasks: logging method calls, handling exceptions, and measuring performance. This often results in the same try-catch, Debug.Log, and Stopwatch code scattered throughout your project.

❌ Before AOP:

public async UniTask<PlayerData> LoadPlayerData(string playerId)
{
    Debug.Log($"[LoadPlayerData] Entry: {playerId}");
    var stopwatch = Stopwatch.StartNew();

    try 
    {
        var data = await _api.GetPlayerData(playerId);
        stopwatch.Stop();
        Debug.Log($"[LoadPlayerData] Completed in {stopwatch.ElapsedMilliseconds}ms");
        return data;
    }
    catch (Exception ex)
    {
        Debug.LogError($"[LoadPlayerData] Error: {ex.Message}");
        throw;
    }
}

✨ The Solution: Write Aspects, Not Boilerplate!

This plugin allows you to encapsulate cross-cutting concerns into reusable attributes. Focus on your core logic and let aspects handle the rest.

βœ… With AOP:

[LogMethod]
[ProfileMethod]
[HandleExceptions]
public async UniTask<PlayerData> LoadPlayerData(string playerId)
{
    var data = await _api.GetPlayerData(playerId);
    return data;
}

πŸ› οΈ Installation and Usage

Step 1: Install Dependencies via UPM

Add the following packages in the Unity Package Manager using the "Add package from git URL..." option:

https://github.com/vovgou/loxodon-framework.git?path=/Loxodon.Framework.Fody/Packages/com.vovgou.loxodon-framework-fody
https://github.com/finerace/MethodBoundaryAspect.Fody-for-Unity.git?path=/com.finerace.loxodon.fody.methodboundaryaspect

Step 2: Configure FodyWeavers.xml

Create or update the FodyWeavers.xml file in the Assets/LoxodonFramework/Editor/AppData/ folder to enable the aspect weaver.

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <AssemblyNames>
    <Item>Assembly-CSharp</Item>
    <!-- Add your other assemblies (asmdef) here if you have them -->
  </AssemblyNames>
  <MethodBoundaryAspect />
</Weavers>

Important

You must list all assemblies (asmdef) where you intend to use aspects in FodyWeavers.xml.

Note

🧠 More on the development...

Supporting async/await methods, especially with a modern library like UniTask, is a non-trivial task for compile-time tools. The C# compiler transforms async methods into complex, hidden state machine classes.

The key achievement of this plugin is its ability to correctly identify these generated classes and weave code into them. This ensures that your aspects work reliably with UniTask, async lambdas, and other modern C# features, which is often a challenge for older AOP tools in Unity.


πŸ“– Documentation

Core Functionality

The plugin provides three entry points into a method's lifecycle that you can override in your aspect class:

  • OnEntry(MethodExecutionArgs args) β€” called before the method body executes.

  • OnExit(MethodExecutionArgs args) β€” called after the method successfully completes, or after OnException if the exception was suppressed.

  • OnException(MethodExecutionArgs args) β€” called if an unhandled exception occurs within the method. It allows you to centralize error handling, logging, and control the execution flow.

Execution Context: MethodExecutionArgs

Each of these methods receives a MethodExecutionArgs object (args), which contains all information about the call:

  • args.Instance: The object instance on which the method was called (this). Will be null for static methods.
  • args.Method: Information about the method itself (MethodInfo).
  • args.Arguments: An object[] array containing the method's input arguments.
  • args.ReturnValue: An object containing the return value.
  • args.Exception: The caught exception. Only available in OnException.
  • args.MethodExecutionTag: An object that can be used to pass data between OnEntry and OnExit/OnException (e.g., for a Stopwatch).
  • args.FlowBehavior: Allows you to control the execution flow after the aspect finishes.
    • FlowBehavior.Continue (default): Continue execution. In OnException, this suppresses the exception.
    • FlowBehavior.RethrowException: Re-throws the exception (used in OnException).
    • FlowBehavior.Return: Immediately exits the method without executing its body (if used in OnEntry).

Aspect Execution Order

If a method has multiple aspect attributes, they work like nesting dolls or a stack (LIFO - Last In, First Out):

[AspectA]
[AspectB]
public void MyMethod() { /* ... */ }

The order of execution will be:

  1. AspectA.OnEntry
  2. AspectB.OnEntry
  3. Execution of MyMethod
  4. AspectB.OnExit
  5. AspectA.OnExit

In case of an exception:

  1. AspectA.OnEntry
  2. AspectB.OnEntry
  3. Exception in MyMethod
  4. AspectB.OnException
  5. AspectA.OnException
  6. AspectB.OnExit (if the exception was suppressed in OnException)
  7. AspectA.OnExit (if the exception was suppressed in OnException)

Other Features

  • Class-Level Attributes: You can apply an aspect to an entire class, and it will automatically be applied to all methods within that class (including private ones).
  • Support for yield Iterators: Aspects are applied to each MoveNext() call of the compiler-generated state machine. This means OnEntry/OnExit will trigger on every iteration of a foreach loop.

❗ Key Limitations and Pitfalls

Important

Please be aware of the following technical limitations when working with MethodBoundaryAspect:

  1. Modifying arguments and return values is not supported for async methods.

    • Reason: The compiler copies arguments into the state machine's fields before OnEntry is called. Modifying args.Arguments or args.ReturnValue will not affect the execution of the asynchronous code.
  2. Lack of Support for async void and async UniTaskVoid

    • Problem: For methods declared as async void or async UniTaskVoid, the plugin will be unable to generate correct IL code.

    • Reason: This is a fundamental limitation of the "fire-and-forget" design of these methods in C#. They do not return a Task or UniTask object that can be monitored for completion. Therefore, the plugin has no way of knowing when the method finishes or throws an exception.

    • Recommendation: When working with aspects, always prefer async UniTask or async Task. This not only ensures the plugin works correctly but also makes your code more reliable and predictable.

  3. Using arg.FlowBehavior = FlowBehavior.Return in OnEntry is not supported for async methods.

    • Reason: The aspect is synchronous code and cannot correctly generate an asynchronous result (Task or UniTask) for an immediate return. This would lead to runtime errors.
  4. Suppressing exceptions in methods that return a value (other than Task, UniTask, or void) can cause a NullReferenceException.

    • The calling code must be prepared to handle such default values.

🧩 Usage Examples

Creating your own aspect is simple. Inherit from OnMethodBoundaryAspect and override the methods you need.

1. Logging Method Entry and Exit

using MethodBoundaryAspect.Fody.Attributes;
using Debug = UnityEngine.Debug;

public class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        if (args.Arguments.Length <= 0)
            Debug.Log($"[{args.Method.Name}] Entry");
        else
            Debug.Log($"[{args.Method.Name}] Entry with: {string.Join(" ,", args.Arguments)}");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        if (args.ReturnValue == null)
            Debug.Log($"[{args.Method.Name}] Exit");
        else
            Debug.Log($"[{args.Method.Name}] Exit with: {args.ReturnValue}");
    }
}
// Usage:
[Log]
public int Add(int a, int b) => a + b;

2. Profiling Method Execution Time

using MethodBoundaryAspect.Fody.Attributes;
using Debug = UnityEngine.Debug;
using System.Diagnostics;

public class ProfileAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        // Use MethodExecutionTag to store the stopwatch between OnEntry and OnExit
        args.MethodExecutionTag = Stopwatch.StartNew();
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        var stopwatch = (Stopwatch)args.MethodExecutionTag;
        stopwatch.Stop();

        Debug.Log($"[{args.Method.Name}] Completed in {stopwatch.ElapsedMilliseconds}ms");
    }
}
// Usage:
[Profile]
public async UniTask LoadSomeData() => await UniTask.Delay(100);

3. Safe Execution and Error Handling

using MethodBoundaryAspect.Fody.Attributes;
using Debug = UnityEngine.Debug;
using System;

public class HandleExceptionAttribute : OnMethodBoundaryAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
        Debug.LogError($"[{args.Method.Name}] Exception: {args.Exception.Message}");

        // Suppress the exception and continue execution
        args.FlowBehavior = FlowBehavior.Continue;
    }
}
// Usage:
[HandleException]
public void MayThrowException() => throw new Exception("Something went wrong!");

πŸ“‹ Requirements and Information

  • Unity Version: 2021.3 or newer

  • Scripting Backend: Mono or IL2CPP

  • Feedback: IL-level code weaving is a deep and complex integration into the Unity engine. Despite extensive testing, unique edge cases can always arise. If you encounter a bug or unstable behavior, please create an Issue. Your feedback is invaluable for improving the plugin!

Changelog

v2.0.0

This is a major update focused on improving stability, fixing critical bugs with async methods, and adding new functionality.

✨ New Features

  • Class-Level Attribute Support: You can now apply aspects to an entire class, significantly reducing code duplication.
  • Partial yield Iterator Support: Aspects are now correctly applied to the MoveNext() method of the state machine, allowing you to track each iteration.
  • Increased Stability: The plugin is now more predictable and reliable in complex scenarios.

πŸ› Bug Fixes

  • Incorrect behavior with async methods (Task/UniTask):
    • OnExit is now correctly called after the final async operation completes, not synchronously at the start.
    • Fixed a Deadlock that occurred when handling exceptions in async methods (FlowBehavior.Continue), which caused an infinite await.
    • Fixed a Race Condition that could cause OnExit to fire unpredictably.
  • Incorrect FlowBehavior.Return behavior: When execution is aborted via OnEntry, OnExit is now correctly called for all aspects in the chain.
  • Broken OnExit chain during exceptions: When an exception is handled by one aspect (FlowBehavior.Continue), OnExit is now correctly called for all preceding aspects.

v1.1.2

  • Added support for anonymous methods and lambdas
  • Fixed handling of compiler-generated classes
  • Improved code weaving stability

v1.1.1

  • Fixed bugs in async methods with UniTask
  • Improved compatibility with UniTask

v1.1.0

  • Added support for UniTask
  • Automatic detection of async state machines
  • Optimized async/await handling
Acknowledgements...

This plugin is an upgraded version based on the work of the following projects:

The key contribution of this version is deep integration and reliable support for modern C# asynchronous features like UniTask.












English English Version icon

logo

ΠŸΠΈΡˆΠΈΡ‚Π΅ чистый C#-ΠΊΠΎΠ΄ Π±Π΅Π· Π²ΠΎΠ΄Ρ‹ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ АспСктно-ΠžΡ€ΠΈΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ.

GitHub Stars Latest Release License Unity Version Status


TL;DR

  • ✨ AOP (Aspect-Oriented Programming) для Unity 2021.3+ с ΠΏΠΎΠ»Π½ΠΎΠΉ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ UniTask/async.
  • πŸš€ Π£Π±ΠΈΡ€Π°Π΅Ρ‚ Π±ΠΎΠΉΠ»Π΅Ρ€ΠΏΠ»Π΅ΠΉΡ‚: Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅, ΠΏΡ€ΠΎΡ„Π°ΠΉΠ»ΠΈΠ½Π³, ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок, ΠΈ Ρ‚.Π΄. β†’ Π΄ΠΎ βˆ’60% ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΡΡŽΡ‰Π΅Π³ΠΎΡΡ ΠΊΠΎΠ΄Π° ΠΈ большС.
  • ⚑️ НулСвая Π½Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π² Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ΅ β€” вСсь ΠΊΠΎΠ΄ внСдряСтся Π½Π° этапС компиляции Ρ‡Π΅Ρ€Π΅Π· IL.
  • πŸ› οΈ Π Π°ΡΡˆΠΈΡ€ΡΠ΅ΠΌΠΎΡΡ‚ΡŒ: Π»Π΅Π³ΠΊΠΎ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ свои аспСкты (ΠΊΡΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅, валидация, авторизация ΠΈ Π΄Ρ€.).
  • πŸ“Š УскоряСт Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ Π½Π° ~20–30% Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ… с Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΌ использованиСм асинхронности.
  • πŸ”’ Π‘Ρ‚Π°Π±ΠΈΠ»ΡŒΠ½Π°Ρ Ρ€Π°Π±ΠΎΡ‚Π°: протСстирован с async-лямбдами, стСйт-машинами компилятора ΠΈ IL2CPP-Π±ΠΈΠ»Π΄Π°ΠΌΠΈ.
  • πŸ›‘οΈ ΠΠ°Π΄Ρ‘ΠΆΠ½ΠΎΡΡ‚ΡŒ: Плагин протСстирован Π² 40+ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Ρ… сцСнариСв использования Π½Π° Ρ€Π°Π·Π½Ρ‹Ρ… вСрсиях Unity.
  • 🧩 Π›Π΅Π³ΠΊΠΎ интСгрируСтся Π² ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ Unity-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρ‹ ΠΈ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ Π½Π° Zenject.

🎯 ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: ΠŸΠΎΠ²Ρ‚ΠΎΡ€ΡΡŽΡ‰ΠΈΠΉΡΡ, разбросанный ΠΏΠΎ всСму ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρƒ ΠΊΠΎΠ΄

Π Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° Unity ΠΏΠΎΠ»Π½Π° ΠΎΠ΄Π½ΠΎΡ‚ΠΈΠΏΠ½Ρ‹Ρ… Π·Π°Π΄Π°Ρ‡: Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ², ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ, ΠΈΠ·ΠΌΠ΅Ρ€Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. Π’ ΠΈΡ‚ΠΎΠ³Π΅ ΠΎΠ΄ΠΈΠ½ ΠΈ Ρ‚ΠΎΡ‚ ΠΆΠ΅ ΠΊΠΎΠ΄ с try-catch, Debug.Log ΠΈ Stopwatch оказываСтся разбросан ΠΏΠΎ всСму ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρƒ.

❌ Π”ΠΎ AOP:

public async UniTask<PlayerData> LoadPlayerData(string playerId)
{
    Debug.Log($"[LoadPlayerData] Π’Ρ…ΠΎΠ΄: {playerId}");
    var stopwatch = Stopwatch.StartNew();

    try 
    {
        var data = await _api.GetPlayerData(playerId);
        stopwatch.Stop();
        Debug.Log($"[LoadPlayerData] Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΎ Π·Π° {stopwatch.ElapsedMilliseconds}мс");
        return data;
    }
    catch (Exception ex)
    {
        Debug.LogError($"[LoadPlayerData] Ошибка: {ex.Message}");
        throw;
    }
}

✨ РСшСниС: ΠŸΠΈΡˆΠΈΡ‚Π΅ аспСкты, Π° Π½Π΅ Π²ΠΎΠ΄Ρƒ!

Π­Ρ‚ΠΎΡ‚ ΠΏΠ»Π°Π³ΠΈΠ½ позволяСт ΠΈΠ½ΠΊΠ°ΠΏΡΡƒΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΡΠΊΠ²ΠΎΠ·Π½ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ Π² ΠΏΠ΅Ρ€Π΅ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹Π΅ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Ρ‹. Π‘ΠΊΠΎΠ½Ρ†Π΅Π½Ρ‚Ρ€ΠΈΡ€ΡƒΠΉΡ‚Π΅ΡΡŒ Π½Π° основной Π»ΠΎΠ³ΠΈΠΊΠ΅, Π° ΠΎΡΡ‚Π°Π»ΡŒΠ½ΠΎΠ΅ Π΄ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ аспСктам.

βœ… ПослС AOP:

[LogMethod]
[ProfileMethod]
[HandleExceptions]
public async UniTask<PlayerData> LoadPlayerData(string playerId)
{
    var data = await _api.GetPlayerData(playerId);
    return data;
}

πŸ› οΈ Установка ΠΈ использованиС

Π¨Π°Π³ 1: УстановитС зависимости Ρ‡Π΅Ρ€Π΅Π· UPM

Π”ΠΎΠ±Π°Π²ΡŒΡ‚Π΅ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ Π² Unity Package Manager, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΎΠΏΡ†ΠΈΡŽ "Add package from git URL...":

https://github.com/vovgou/loxodon-framework.git?path=/Loxodon.Framework.Fody/Packages/com.vovgou.loxodon-framework-fody
https://github.com/finerace/MethodBoundaryAspect.Fody-for-Unity.git?path=/com.finerace.loxodon.fody.methodboundaryaspect

Π¨Π°Π³ 2: НастройтС FodyWeavers.xml

Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ ΠΈΠ»ΠΈ ΠΎΠ±Π½ΠΎΠ²ΠΈΡ‚Π΅ Ρ„Π°ΠΉΠ» FodyWeavers.xml Π² ΠΏΠ°ΠΏΠΊΠ΅ Assets/LoxodonFramework/Editor/AppData/, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ аспСктов.

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <AssemblyNames>
    <Item>Assembly-CSharp</Item>
    <!-- Π—Π΄Π΅ΡΡŒ Π½ΡƒΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ваши сборки (asmdef), Ссли ΠΎΠ½ΠΈ Π΅ΡΡ‚ΡŒ -->
  </AssemblyNames>
  <MethodBoundaryAspect />
</Weavers>

Important

Π’ FodyWeavers.xml Π½ΡƒΠΆΠ½ΠΎ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ всС сборки (asmdef), Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ аспСкты.

Note

🧠 ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½Π΅Π΅ ΠΎ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅...

ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° async/await ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ², особСнно с Ρ‚Π°ΠΊΠΎΠΉ соврСмСнной Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΎΠΉ ΠΊΠ°ΠΊ UniTask β€” это Π½Π΅Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»ΡŒΠ½Π°Ρ Π·Π°Π΄Π°Ρ‡Π° для инструмСнтов, Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΡ… Π½Π° этапС компиляции. ΠšΠΎΠΌΠΏΠΈΠ»ΡΡ‚ΠΎΡ€ C# ΠΏΡ€Π΅Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ async-ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Π² слоТныС, скрытыС классы-ΡΡ‚Π΅ΠΉΡ‚ΠΌΠ°ΡˆΠΈΠ½Ρ‹.

Π“Π»Π°Π²Π½ΠΎΠ΅ достиТСниС этого ΠΏΠ»Π°Π³ΠΈΠ½Π° β€” Π΅Π³ΠΎ ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡ‚ΡŒ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ эти сгСнСрированныС классы ΠΈ Π²Π½Π΅Π΄Ρ€ΡΡ‚ΡŒ Π² Π½ΠΈΡ… ΠΊΠΎΠ΄. Π­Ρ‚ΠΎ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ ваши аспСкты Π±ΡƒΠ΄ΡƒΡ‚ Π½Π°Π΄Π΅ΠΆΠ½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с UniTask, async-лямбдами ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ соврСмСнными возмоТностями C#, Ρ‡Ρ‚ΠΎ часто являСтся ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΎΠΉ Π² ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΡ… AOP-инструмСнтах для Unity.


πŸ“– ДокумСнтация

Основная Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ

Плагин прСдоставляСт Ρ‚Ρ€ΠΈ Ρ‚ΠΎΡ‡ΠΊΠΈ Π²Ρ…ΠΎΠ΄Π° Π² ΠΆΠΈΠ·Π½Π΅Π½Π½Ρ‹ΠΉ Ρ†ΠΈΠΊΠ» ΠΌΠ΅Ρ‚ΠΎΠ΄Π°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ Π² своём классС-аспСктС:

  • OnEntry(MethodExecutionArgs args) β€” вызываСтся ΠΏΠ΅Ρ€Π΅Π΄ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ΠΌ Ρ‚Π΅Π»Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Π°.

  • OnExit(MethodExecutionArgs args) β€” вызываСтся послС ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠ³ΠΎ выполнСния ΠΌΠ΅Ρ‚ΠΎΠ΄Π°, ΠΈΠ»ΠΈ послС OnException Π² случаС подавлСния ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ.

  • OnException(MethodExecutionArgs args) β€” вызываСтся, Ссли Π²Π½ΡƒΡ‚Ρ€ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»ΠΎ Π½Π΅ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅. ΠŸΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ‚ Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΠΎ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ ошибки, Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΡ… ΠΈ ΡƒΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ дальнСйшим Ρ…ΠΎΠ΄ΠΎΠΌ выполнСния.

ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ выполнСния: MethodExecutionArgs

ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· этих ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ MethodExecutionArgs (args), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ содСрТит всю ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ Π²Ρ‹Π·ΠΎΠ²Π΅:

  • args.Instance: ЭкзСмпляр ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°, Ρƒ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Π²Ρ‹Π·Π²Π°Π½ ΠΌΠ΅Ρ‚ΠΎΠ΄ (this). Для static-ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² Π±ΡƒΠ΄Π΅Ρ‚ null.
  • args.Method: Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ ΠΎ самом ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ (MethodInfo).
  • args.Arguments: Массив object[] с Π²Ρ…ΠΎΠ΄Π½Ρ‹ΠΌΠΈ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π°ΠΌΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°.
  • args.ReturnValue: object, содСрТащий Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
  • args.Exception: ΠŸΠ΅Ρ€Π΅Ρ…Π²Π°Ρ‡Π΅Π½Π½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅. Доступно Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² OnException.
  • args.MethodExecutionTag: object, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π΄Π°Π½Π½Ρ‹Ρ… ΠΌΠ΅ΠΆΠ΄Ρƒ OnEntry ΠΈ OnExit/OnException (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, для Stopwatch).
  • args.FlowBehavior: ΠŸΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ‚ ΡƒΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ Ρ…ΠΎΠ΄ΠΎΠΌ выполнСния послС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ аспСкта.
    • FlowBehavior.Continue (ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ): ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅. Π’ OnException это ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚ ΠΏΠΎΠ΄Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ.
    • FlowBehavior.RethrowException: ΠŸΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎ Π²Ρ‹Π±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ (ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² OnException).
    • FlowBehavior.Return: НСмСдлСнно Π·Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°, Π½Π΅ вызывая Π΅Π³ΠΎ Ρ‚Π΅Π»ΠΎ (Ссли ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² OnEntry).

ΠŸΠΎΡ€ΡΠ΄ΠΎΠΊ выполнСния аспСктов

Если ΠΊ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρƒ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΎ нСсколько Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠ², ΠΎΠ½ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ ΠΏΠΎ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΡƒ "ΠΌΠ°Ρ‚Ρ€Ρ‘ΡˆΠΊΠΈ" ΠΈΠ»ΠΈ стСка (LIFO - Last In, First Out):

[AspectA]
[AspectB]
public void MyMethod() { /* ... */ }

ΠŸΠΎΡ€ΡΠ΄ΠΎΠΊ Π±ΡƒΠ΄Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ:

  1. AspectA.OnEntry
  2. AspectB.OnEntry
  3. Π’Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ MyMethod
  4. AspectB.OnExit
  5. AspectA.OnExit

Π’ случаС ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ:

  1. AspectA.OnEntry
  2. AspectB.OnEntry
  3. Π˜ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π² MyMethod
  4. AspectB.OnException
  5. AspectA.OnException
  6. AspectB.OnExit (Ссли ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π±Ρ‹Π»ΠΎ ΠΏΠΎΠ΄Π°Π²Π»Π΅Π½ΠΎ Π² OnException)
  7. AspectA.OnExit (Ссли ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π±Ρ‹Π»ΠΎ ΠΏΠΎΠ΄Π°Π²Π»Π΅Π½ΠΎ Π² OnException)

Π”Ρ€ΡƒΠ³ΠΈΠ΅ возмоТности

  • Атрибуты Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ класса: Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ аспСкт ΠΊΠΎ всСму классу, ΠΈ ΠΎΠ½ автоматичСски примСнится ΠΊΠΎ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌ этого класса (Π² Ρ‚ΠΎΠΌ числС ΠΈ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΌ).
  • ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° yield-ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ΠΎΠ²: АспСкты ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡŽΡ‚ΡΡ ΠΊ ΠΊΠ°ΠΆΠ΄ΠΎΠΌΡƒ Π²Ρ‹Π·ΠΎΠ²Ρƒ MoveNext() сгСнСрированной компилятором state-ΠΌΠ°ΡˆΠΈΠ½Ρ‹. Π­Ρ‚ΠΎ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ OnEntry/OnExit Π±ΡƒΠ΄ΡƒΡ‚ ΡΡ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΈ foreach.

❗ ΠšΠ»ΡŽΡ‡Π΅Π²Ρ‹Π΅ ограничСния ΠΈ ΠΏΠΎΠ΄Π²ΠΎΠ΄Π½Ρ‹Π΅ ΠΊΠ°ΠΌΠ½ΠΈ

Important

ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, ΡƒΡ‡ΠΈΡ‚Ρ‹Π²Π°ΠΉΡ‚Π΅ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ тСхничСскиС ограничСния ΠΏΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ с MethodBoundaryAspect:

  1. ИзмСнСниС Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΠΎΠ³ΠΎ значСния Π½Π΅ поддСрТиваСтся для async-ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ².

    • ΠŸΡ€ΠΈΡ‡ΠΈΠ½Π°: ΠšΠΎΠΌΠΏΠΈΠ»ΡΡ‚ΠΎΡ€ ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅Ρ‚ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ Π² поля state-ΠΌΠ°ΡˆΠΈΠ½Ρ‹ Π΄ΠΎ Π²Ρ‹Π·ΠΎΠ²Π° OnEntry. ΠœΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡ args.Arguments ΠΈΠ»ΠΈ args.ReturnValue Π½Π΅ повлияСт Π½Π° Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ асинхронного ΠΊΠΎΠ΄Π°.
  2. ΠžΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΠΈΠ΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΈ async void ΠΈ async UniTaskVoid

    • ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: Для ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ², ΠΎΠ±ΡŠΡΠ²Π»Π΅Π½Π½Ρ‹Ρ… ΠΊΠ°ΠΊ async void ΠΈΠ»ΠΈ async UniTaskVoid, ΠΏΠ»Π°Π³ΠΈΠ½ Π½Π΅ смоТСт ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΡ€Ρ€Π΅Ρ‚Π½Ρ‹ΠΉ IL-ΠΊΠΎΠ΄.

    • ΠŸΡ€ΠΈΡ‡ΠΈΠ½Π°: Π­Ρ‚ΠΎ Ρ„ΡƒΠ½Π΄Π°ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠ΅ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅, связанноС с Π΄ΠΈΠ·Π°ΠΉΠ½ΠΎΠΌ "fire-and-forget" ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² Π² C#. Π£ Π½ΠΈΡ… Π½Π΅Ρ‚ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° (Task/UniTask), Π·Π° Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ΠΌ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ Π±Ρ‹ ΡΠ»Π΅Π΄ΠΈΡ‚ΡŒ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΠΏΠ»Π°Π³ΠΈΠ½ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΡƒΠ·Π½Π°Ρ‚ΡŒ, ΠΊΠΎΠ³Π΄Π° ΠΌΠ΅Ρ‚ΠΎΠ΄ Π·Π°Π²Π΅Ρ€ΡˆΠΈΠ»ΡΡ ΠΈΠ»ΠΈ выбросил ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅.

    • РСкомСндация: ΠŸΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ с аспСктами всСгда ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡ΠΈΡ‚Π°ΠΉΡ‚Π΅ async UniTask ΠΈΠ»ΠΈ async Task. Π­Ρ‚ΠΎ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ обСспСчит ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΡƒΡŽ Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΏΠ»Π°Π³ΠΈΠ½Π°, Π½ΠΎ ΠΈ сдСлаСт ваш ΠΊΠΎΠ΄ Π² Ρ†Π΅Π»ΠΎΠΌ Π±ΠΎΠ»Π΅Π΅ Π½Π°Π΄Ρ‘ΠΆΠ½Ρ‹ΠΌ ΠΈ прСдсказуСмым.

  3. ИспользованиС arg.FlowBehavior = FlowBehavior.Return Π² OnEntry Π½Π΅ поддСрТиваСтся для async-ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ².

    • ΠŸΡ€ΠΈΡ‡ΠΈΠ½Π°: АспСкт β€” это синхронный ΠΊΠΎΠ΄, ΠΈ ΠΎΠ½ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ асинхронный Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ (Task ΠΈΠ»ΠΈ UniTask) для Π½Π΅ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠ³ΠΎ Π²ΠΎΠ·Π²Ρ€Π°Ρ‚Π°. Π­Ρ‚ΠΎ ΠΏΡ€ΠΈΠ²Π΅Π»ΠΎ Π±Ρ‹ ΠΊ ошибкам Π²ΠΎ врСмя выполнСния.
  4. ПодавлСниС ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ Π² ΠΌΠ΅Ρ‚ΠΎΠ΄Π°Ρ…, Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‰ΠΈΡ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ (Π½Π΅ считая Task, UniTask ΠΈ void), ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ NullReferenceException.

    • Π’Ρ‹Π·Ρ‹Π²Π°ΡŽΡ‰ΠΈΠΉ ΠΊΠΎΠ΄ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Π³ΠΎΡ‚ΠΎΠ² ΠΊ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Ρ‚Π°ΠΊΠΈΡ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ.

🧩 ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ использования

Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ собствСнный аспСкт ΠΎΡ‡Π΅Π½ΡŒ просто. УнаслСдуйтС класс ΠΎΡ‚ OnMethodBoundaryAspect ΠΈ ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚Π΅ Π½ΡƒΠΆΠ½Ρ‹Π΅ Π²Π°ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹.

1. Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π²Ρ…ΠΎΠ΄Π° ΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π° ΠΈΠ· ΠΌΠ΅Ρ‚ΠΎΠ΄Π°

using MethodBoundaryAspect.Fody.Attributes;
using Debug = UnityEngine.Debug;

public class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        if (args.Arguments.Length <= 0)
            Debug.Log($"[{args.Method.Name}] Π’Ρ…ΠΎΠ΄");
        else
            Debug.Log($"[{args.Method.Name}] Π’Ρ…ΠΎΠ΄: {string.Join(" ,", args.Arguments)}");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        if (args.ReturnValue == null)
            Debug.Log($"[{args.Method.Name}] Π’Ρ‹Ρ…ΠΎΠ΄");
        else
            Debug.Log($"[{args.Method.Name}] Π’Ρ‹Ρ…ΠΎΠ΄: {args.ReturnValue}");
    }
}
// ИспользованиС:
[Log]
public int Add(int a, int b) => a + b;

2. ΠŸΡ€ΠΎΡ„ΠΈΠ»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ выполнСния ΠΌΠ΅Ρ‚ΠΎΠ΄Π°

using MethodBoundaryAspect.Fody.Attributes;
using Debug = UnityEngine.Debug;
using System.Diagnostics;

public class ProfileAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        // Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ MethodExecutionTag для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π΄Π°Π½Π½Ρ‹Ρ… ΠΌΠ΅ΠΆΠ΄Ρƒ OnEntry ΠΈ OnExit
        args.MethodExecutionTag = Stopwatch.StartNew();
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        var stopwatch = (Stopwatch)args.MethodExecutionTag;
        stopwatch.Stop();

        Debug.Log($"[{args.Method.Name}] Π—Π°Π²Π΅Ρ€ΡˆΡ‘Π½ Π·Π° {stopwatch.ElapsedMilliseconds}ms");
    }
}
// ИспользованиС:
[Profile]
public async UniTask LoadSomeData() => await UniTask.Delay(100);

3. БСзопасноС Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΈ ловля ошибок

using MethodBoundaryAspect.Fody.Attributes;
using Debug = UnityEngine.Debug;

public class HandleExceptionAttribute : OnMethodBoundaryAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
        Debug.LogError($"[{args.Method.Name}] Exception");

        args.FlowBehavior = FlowBehavior.Continue;
    }
}
// ИспользованиС:
[HandleException]
public void MayThrowException() => throw new Exception("Π§Ρ‚ΠΎ-Ρ‚ΠΎ пошло Π½Π΅ Ρ‚Π°ΠΊ!");```

πŸ“‹ ВрСбования ΠΈ информация

  • ВСрсия Unity: 2021.3 ΠΈΠ»ΠΈ Π½ΠΎΠ²Π΅Π΅

  • Scripting Backend: Mono ΠΈΠ»ΠΈ IL2CPP

  • ΠžΠ±Ρ€Π°Ρ‚Π½Π°Ρ связь: Π’ΠΏΠ»Π΅Ρ‚Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π° Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ IL β€” это глубокая ΠΈ слоТная интСграция Π² Π΄Π²ΠΈΠΆΠΎΠΊ Unity. НСсмотря Π½Π° ΠΎΠ±ΡˆΠΈΡ€Π½ΠΎΠ΅ тСстированиС, всСгда ΠΌΠΎΠ³ΡƒΡ‚ Π½Π°ΠΉΡ‚ΠΈΡΡŒ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ ΠΏΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π½Ρ‹Π΅ случаи. Если Π²Ρ‹ ΡΡ‚ΠΎΠ»ΠΊΠ½ΡƒΠ»ΠΈΡΡŒ с ошибкой ΠΈΠ»ΠΈ Π½Π΅ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½Ρ‹ΠΌ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ΠΌ, поТалуйста, создайтС Issue. Π’Π°ΡˆΠ° обратная связь бСсцСнна для ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ ΠΏΠ»Π°Π³ΠΈΠ½Π°!

Π˜ΡΡ‚ΠΎΡ€ΠΈΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ (Changelog)

v2.0.0

Π­Ρ‚ΠΎ ΠΊΡ€ΡƒΠΏΠ½ΠΎΠ΅ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅, Π½Π°ΠΏΡ€Π°Π²Π»Π΅Π½Π½ΠΎΠ΅ Π½Π° ΠΏΠΎΠ²Ρ‹ΡˆΠ΅Π½ΠΈΠ΅ ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΠΈ, исправлСниС критичСских ошибок с async-ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ.

✨ Новая Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ

  • ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠ² Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ класса: Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒ аспСкты ΠΊΠΎ всСму классу, Ρ‡Ρ‚ΠΎ Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΡƒΠΌΠ΅Π½ΡŒΡˆΠ°Π΅Ρ‚ Π΄ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π°.
  • Частичная ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° yield-ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ΠΎΠ²: АспСкты Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡŽΡ‚ΡΡ ΠΊ MoveNext()-ΠΌΠ΅Ρ‚ΠΎΠ΄Ρƒ state-ΠΌΠ°ΡˆΠΈΠ½Ρ‹, позволяя ΠΎΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Ρ‚ΡŒ ΠΊΠ°ΠΆΠ΄ΡƒΡŽ ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΡŽ.
  • ΠŸΠΎΠ²Ρ‹ΡˆΠ΅Π½Π½Π°Ρ ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΡŒ: Плагин стал Π±ΠΎΠ»Π΅Π΅ прСдсказуСмым ΠΈ Π½Π°Π΄Π΅ΠΆΠ½Ρ‹ΠΌ Π² слоТных сцСнариях.

πŸ› Π˜ΡΠΏΡ€Π°Π²Π»Π΅Π½Π½Ρ‹Π΅ ошибки

  • НСкоррСктная Ρ€Π°Π±ΠΎΡ‚Π° с async ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ (Task/UniTask):
    • OnExit Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ вызываСтся ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ послС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ послСднСй async-ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ, Π° Π½Π΅ синхронно Π² Π½Π°Ρ‡Π°Π»Π΅.
    • УстранСн Deadlock, возникавший ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ Π² async-ΠΌΠ΅Ρ‚ΠΎΠ΄Π°Ρ… (FlowBehavior.Continue), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΠ» ΠΊ Π²Π΅Ρ‡Π½ΠΎΠΌΡƒ await.
    • Π˜ΡΠΏΡ€Π°Π²Π»Π΅Π½Π° Race Condition, ΠΈΠ·-Π·Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ OnExit ΠΌΠΎΠ³ ΡΡ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ нСпрСдсказуСмо.
  • НСкоррСктная Ρ€Π°Π±ΠΎΡ‚Π° FlowBehavior.Return: ΠŸΡ€ΠΈ ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°Π½ΠΈΠΈ выполнСния ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Ρ‡Π΅Ρ€Π΅Π· OnEntry Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ вызываСтся OnExit для всСх аспСктов Π² Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅.
  • ΠΠ°Ρ€ΡƒΡˆΠ΅Π½ΠΈΠ΅ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ OnExit ΠΏΡ€ΠΈ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡΡ…: ΠŸΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΎΠ΄Π½ΠΈΠΌ ΠΈΠ· аспСктов (FlowBehavior.Continue) Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ вызываСтся OnExit для всСх ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰ΠΈΡ… аспСктов.

v1.1.2

  • Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° Π°Π½ΠΎΠ½ΠΈΠΌΠ½Ρ‹Ρ… ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² ΠΈ лямбд
  • Π˜ΡΠΏΡ€Π°Π²Π»Π΅Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° сгСнСрированных компилятором классов
  • Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π° ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΡŒ "вплСтСния" ΠΊΠΎΠ΄Π°

v1.1.1

  • Π˜ΡΠΏΡ€Π°Π²Π»Π΅Π½Ρ‹ ошибки Π² асинхронных ΠΌΠ΅Ρ‚ΠΎΠ΄Π°Ρ… с UniTask
  • Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π° ΡΠΎΠ²ΠΌΠ΅ΡΡ‚ΠΈΠΌΠΎΡΡ‚ΡŒ с UniTask

v1.1.0

  • Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° UniTask
  • АвтоматичСскоС ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ асинхронных стСйт-машин
  • ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° async/await
Благодарности...

Π­Ρ‚ΠΎΡ‚ ΠΏΠ»Π°Π³ΠΈΠ½ являСтся ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠΉ вСрсиСй, основанной Π½Π° Ρ€Π°Π±ΠΎΡ‚Π΅ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΡ… ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ²:

  • MethodBoundaryAspect.Fody β€” ΠžΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½Π°Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° для .NET, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ основан ΠΏΠ»Π°Π³ΠΈΠ½.
  • Loxodon Framework β€” Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ, ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²ΠΈΠ²ΡˆΠΈΠΉ ΠΏΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½ΡƒΡŽ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΡŽ Fody Π² срСду Unity.

ΠšΠ»ΡŽΡ‡Π΅Π²ΠΎΠΉ Π²ΠΊΠ»Π°Π΄ этой вСрсии β€” глубокая интСграция ΠΈ надСТная ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° соврСмСнных асинхронных возмоТностСй C#, Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ UniTask.