- β¨ 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.
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;
}
}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;
}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
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.
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 afterOnExceptionif 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.
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 benullforstaticmethods.args.Method: Information about the method itself (MethodInfo).args.Arguments: Anobject[]array containing the method's input arguments.args.ReturnValue: Anobjectcontaining the return value.args.Exception: The caught exception. Only available inOnException.args.MethodExecutionTag: Anobjectthat can be used to pass data betweenOnEntryandOnExit/OnException(e.g., for aStopwatch).args.FlowBehavior: Allows you to control the execution flow after the aspect finishes.FlowBehavior.Continue(default): Continue execution. InOnException, this suppresses the exception.FlowBehavior.RethrowException: Re-throws the exception (used inOnException).FlowBehavior.Return: Immediately exits the method without executing its body (if used inOnEntry).
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:
AspectA.OnEntryAspectB.OnEntry- Execution of
MyMethod AspectB.OnExitAspectA.OnExit
In case of an exception:
AspectA.OnEntryAspectB.OnEntry- Exception in
MyMethod AspectB.OnExceptionAspectA.OnExceptionAspectB.OnExit(if the exception was suppressed inOnException)AspectA.OnExit(if the exception was suppressed inOnException)
- 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
yieldIterators: Aspects are applied to eachMoveNext()call of the compiler-generated state machine. This meansOnEntry/OnExitwill trigger on every iteration of aforeachloop.
Important
Please be aware of the following technical limitations when working with MethodBoundaryAspect:
-
Modifying arguments and return values is not supported for async methods.
- Reason: The compiler copies arguments into the state machine's fields before
OnEntryis called. Modifyingargs.Argumentsorargs.ReturnValuewill not affect the execution of the asynchronous code.
- Reason: The compiler copies arguments into the state machine's fields before
-
Lack of Support for
async voidandasync 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
TaskorUniTaskobject 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 UniTaskorasync Task. This not only ensures the plugin works correctly but also makes your code more reliable and predictable.
-
-
Using
arg.FlowBehavior = FlowBehavior.ReturninOnEntryis not supported for async methods.- Reason: The aspect is synchronous code and cannot correctly generate an asynchronous result (
TaskorUniTask) for an immediate return. This would lead to runtime errors.
- Reason: The aspect is synchronous code and cannot correctly generate an asynchronous result (
-
Suppressing exceptions in methods that return a value (other than
Task,UniTask, orvoid) can cause aNullReferenceException.- The calling code must be prepared to handle such default values.
Creating your own aspect is simple. Inherit from OnMethodBoundaryAspect and override the methods you need.
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;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);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!");-
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
This is a major update focused on improving stability, fixing critical bugs with async methods, and adding new functionality.
- Class-Level Attribute Support: You can now apply aspects to an entire class, significantly reducing code duplication.
- Partial
yieldIterator Support: Aspects are now correctly applied to theMoveNext()method of the state machine, allowing you to track each iteration. - Increased Stability: The plugin is now more predictable and reliable in complex scenarios.
- Incorrect behavior with
asyncmethods (Task/UniTask):OnExitis now correctly called after the finalasyncoperation completes, not synchronously at the start.- Fixed a Deadlock that occurred when handling exceptions in
asyncmethods (FlowBehavior.Continue), which caused an infiniteawait. - Fixed a Race Condition that could cause
OnExitto fire unpredictably.
- Incorrect
FlowBehavior.Returnbehavior: When execution is aborted viaOnEntry,OnExitis now correctly called for all aspects in the chain. - Broken
OnExitchain during exceptions: When an exception is handled by one aspect (FlowBehavior.Continue),OnExitis now correctly called for all preceding aspects.
- Added support for anonymous methods and lambdas
- Fixed handling of compiler-generated classes
- Improved code weaving stability
- Fixed bugs in async methods with UniTask
- Improved compatibility with UniTask
- 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:
- MethodBoundaryAspect.Fody β The original .NET library on which this plugin is based.
- Loxodon Framework β The framework that provided the initial Fody integration for the Unity environment.
The key contribution of this version is deep integration and reliable support for modern C# asynchronous features like UniTask.
ΠΠΈΡΠΈΡΠ΅ ΡΠΈΡΡΡΠΉ C#-ΠΊΠΎΠ΄ Π±Π΅Π· Π²ΠΎΠ΄Ρ Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΠΡΠΏΠ΅ΠΊΡΠ½ΠΎ-ΠΡΠΈΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠΡΠΎΠ³ΡΠ°ΠΌΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ.
- β¨ 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;
}ΠΠΎΠ±Π°Π²ΡΡΠ΅ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΠΏΠ°ΠΊΠ΅ΡΡ Π² 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
Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΠΈΠ»ΠΈ ΠΎΠ±Π½ΠΎΠ²ΠΈΡΠ΅ ΡΠ°ΠΉΠ» 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 (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() { /* ... */ }ΠΠΎΡΡΠ΄ΠΎΠΊ Π±ΡΠ΄Π΅Ρ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ:
AspectA.OnEntryAspectB.OnEntry- ΠΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅
MyMethod AspectB.OnExitAspectA.OnExit
Π ΡΠ»ΡΡΠ°Π΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ:
AspectA.OnEntryAspectB.OnEntry- ΠΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²
MyMethod AspectB.OnExceptionAspectA.OnExceptionAspectB.OnExit(Π΅ΡΠ»ΠΈ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π±ΡΠ»ΠΎ ΠΏΠΎΠ΄Π°Π²Π»Π΅Π½ΠΎ Π²OnException)AspectA.OnExit(Π΅ΡΠ»ΠΈ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π±ΡΠ»ΠΎ ΠΏΠΎΠ΄Π°Π²Π»Π΅Π½ΠΎ Π²OnException)
- ΠΡΡΠΈΠ±ΡΡΡ Π½Π° ΡΡΠΎΠ²Π½Π΅ ΠΊΠ»Π°ΡΡΠ°: ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΏΡΠΈΠΌΠ΅Π½ΠΈΡΡ Π°ΡΠΏΠ΅ΠΊΡ ΠΊΠΎ Π²ΡΠ΅ΠΌΡ ΠΊΠ»Π°ΡΡΡ, ΠΈ ΠΎΠ½ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΏΡΠΈΠΌΠ΅Π½ΠΈΡΡΡ ΠΊΠΎ ΠΌΠ΅ΡΠΎΠ΄Π°ΠΌ ΡΡΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ° (Π² ΡΠΎΠΌ ΡΠΈΡΠ»Π΅ ΠΈ ΠΏΡΠΈΠ²Π°ΡΠ½ΡΠΌ).
- ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ°
yield-ΠΈΡΠ΅ΡΠ°ΡΠΎΡΠΎΠ²: ΠΡΠΏΠ΅ΠΊΡΡ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡΡΡ ΠΊ ΠΊΠ°ΠΆΠ΄ΠΎΠΌΡ Π²ΡΠ·ΠΎΠ²ΡMoveNext()ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠΉ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡΠΎΠΌ state-ΠΌΠ°ΡΠΈΠ½Ρ. ΠΡΠΎ ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ, ΡΡΠΎOnEntry/OnExitΠ±ΡΠ΄ΡΡ ΡΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΈΡΠ΅ΡΠ°ΡΠΈΠΈforeach.
Important
ΠΠΎΠΆΠ°Π»ΡΠΉΡΡΠ°, ΡΡΠΈΡΡΠ²Π°ΠΉΡΠ΅ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΡΠ΅Ρ Π½ΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ ΠΏΡΠΈ ΡΠ°Π±ΠΎΡΠ΅ Ρ MethodBoundaryAspect:
-
ΠΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΎΠ² ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΠΎΠ³ΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΡ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΡΡΡ Π΄Π»Ρ async-ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ².
- ΠΡΠΈΡΠΈΠ½Π°: ΠΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡ ΠΊΠΎΠΏΠΈΡΡΠ΅Ρ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ Π² ΠΏΠΎΠ»Ρ state-ΠΌΠ°ΡΠΈΠ½Ρ Π΄ΠΎ Π²ΡΠ·ΠΎΠ²Π°
OnEntry. ΠΠΎΠ΄ΠΈΡΠΈΠΊΠ°ΡΠΈΡargs.ArgumentsΠΈΠ»ΠΈargs.ReturnValueΠ½Π΅ ΠΏΠΎΠ²Π»ΠΈΡΠ΅Ρ Π½Π° Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π°ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°.
- ΠΡΠΈΡΠΈΠ½Π°: ΠΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡ ΠΊΠΎΠΏΠΈΡΡΠ΅Ρ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ Π² ΠΏΠΎΠ»Ρ state-ΠΌΠ°ΡΠΈΠ½Ρ Π΄ΠΎ Π²ΡΠ·ΠΎΠ²Π°
-
ΠΡΡΡΡΡΡΠ²ΠΈΠ΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΈ
async voidΠΈasync UniTaskVoid-
ΠΡΠΎΠ±Π»Π΅ΠΌΠ°: ΠΠ»Ρ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ², ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π½ΡΡ ΠΊΠ°ΠΊ
async voidΠΈΠ»ΠΈasync UniTaskVoid, ΠΏΠ»Π°Π³ΠΈΠ½ Π½Π΅ ΡΠΌΠΎΠΆΠ΅Ρ ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ ΠΊΠΎΡΡΠ΅ΡΠ½ΡΠΉ IL-ΠΊΠΎΠ΄. -
ΠΡΠΈΡΠΈΠ½Π°: ΠΡΠΎ ΡΡΠ½Π΄Π°ΠΌΠ΅Π½ΡΠ°Π»ΡΠ½ΠΎΠ΅ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅, ΡΠ²ΡΠ·Π°Π½Π½ΠΎΠ΅ Ρ Π΄ΠΈΠ·Π°ΠΉΠ½ΠΎΠΌ "fire-and-forget" ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² Π² C#. Π£ Π½ΠΈΡ Π½Π΅Ρ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ° (Task/UniTask), Π·Π° Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΈΠ΅ΠΌ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ Π±Ρ ΡΠ»Π΅Π΄ΠΈΡΡ. ΠΠΎΡΡΠΎΠΌΡ ΠΏΠ»Π°Π³ΠΈΠ½ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ ΡΠ·Π½Π°ΡΡ, ΠΊΠΎΠ³Π΄Π° ΠΌΠ΅ΡΠΎΠ΄ Π·Π°Π²Π΅ΡΡΠΈΠ»ΡΡ ΠΈΠ»ΠΈ Π²ΡΠ±ΡΠΎΡΠΈΠ» ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅.
-
Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°ΡΠΈΡ: ΠΡΠΈ ΡΠ°Π±ΠΎΡΠ΅ Ρ Π°ΡΠΏΠ΅ΠΊΡΠ°ΠΌΠΈ Π²ΡΠ΅Π³Π΄Π° ΠΏΡΠ΅Π΄ΠΏΠΎΡΠΈΡΠ°ΠΉΡΠ΅ async UniTask ΠΈΠ»ΠΈ async Task. ΠΡΠΎ Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠΈΡ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΡ ΡΠ°Π±ΠΎΡΡ ΠΏΠ»Π°Π³ΠΈΠ½Π°, Π½ΠΎ ΠΈ ΡΠ΄Π΅Π»Π°Π΅Ρ Π²Π°Ρ ΠΊΠΎΠ΄ Π² ΡΠ΅Π»ΠΎΠΌ Π±ΠΎΠ»Π΅Π΅ Π½Π°Π΄ΡΠΆΠ½ΡΠΌ ΠΈ ΠΏΡΠ΅Π΄ΡΠΊΠ°Π·ΡΠ΅ΠΌΡΠΌ.
-
-
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
arg.FlowBehavior = FlowBehavior.ReturnΠ²OnEntryΠ½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΡΡΡ Π΄Π»Ρ async-ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ².- ΠΡΠΈΡΠΈΠ½Π°: ΠΡΠΏΠ΅ΠΊΡ β ΡΡΠΎ ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΡΠΉ ΠΊΠΎΠ΄, ΠΈ ΠΎΠ½ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎ ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ Π°ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΡΠΉ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ (
TaskΠΈΠ»ΠΈUniTask) Π΄Π»Ρ Π½Π΅ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠ³ΠΎ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°. ΠΡΠΎ ΠΏΡΠΈΠ²Π΅Π»ΠΎ Π±Ρ ΠΊ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌ Π²ΠΎ Π²ΡΠ΅ΠΌΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ.
- ΠΡΠΈΡΠΈΠ½Π°: ΠΡΠΏΠ΅ΠΊΡ β ΡΡΠΎ ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΡΠΉ ΠΊΠΎΠ΄, ΠΈ ΠΎΠ½ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎ ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ Π°ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΡΠΉ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ (
-
ΠΠΎΠ΄Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠΉ Π² ΠΌΠ΅ΡΠΎΠ΄Π°Ρ , Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡΠΈΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ (Π½Π΅ ΡΡΠΈΡΠ°Ρ
Task,UniTaskΠΈvoid), ΠΌΠΎΠΆΠ΅Ρ Π²ΡΠ·Π²Π°ΡΡNullReferenceException.- ΠΡΠ·ΡΠ²Π°ΡΡΠΈΠΉ ΠΊΠΎΠ΄ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±ΡΡΡ Π³ΠΎΡΠΎΠ² ΠΊ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ΅ ΡΠ°ΠΊΠΈΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ.
Π‘ΠΎΠ·Π΄Π°ΡΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΠΉ Π°ΡΠΏΠ΅ΠΊΡ ΠΎΡΠ΅Π½Ρ ΠΏΡΠΎΡΡΠΎ. Π£Π½Π°ΡΠ»Π΅Π΄ΡΠΉΡΠ΅ ΠΊΠ»Π°ΡΡ ΠΎΡ OnMethodBoundaryAspect ΠΈ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΠ΅ Π½ΡΠΆΠ½ΡΠ΅ Π²Π°ΠΌ ΠΌΠ΅ΡΠΎΠ΄Ρ.
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;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);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)
ΠΡΠΎ ΠΊΡΡΠΏΠ½ΠΎΠ΅ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅, Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½Π½ΠΎΠ΅ Π½Π° ΠΏΠΎΠ²ΡΡΠ΅Π½ΠΈΠ΅ ΡΡΠ°Π±ΠΈΠ»ΡΠ½ΠΎΡΡΠΈ, ΠΈΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈΡ
ΠΎΡΠΈΠ±ΠΎΠΊ Ρ 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Π΄Π»Ρ Π²ΡΠ΅Ρ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠΈΡ Π°ΡΠΏΠ΅ΠΊΡΠΎΠ².
- ΠΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° Π°Π½ΠΎΠ½ΠΈΠΌΠ½ΡΡ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² ΠΈ Π»ΡΠΌΠ±Π΄
- ΠΡΠΏΡΠ°Π²Π»Π΅Π½Π° ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°Π½Π½ΡΡ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡΠΎΠΌ ΠΊΠ»Π°ΡΡΠΎΠ²
- Π£Π»ΡΡΡΠ΅Π½Π° ΡΡΠ°Π±ΠΈΠ»ΡΠ½ΠΎΡΡΡ "Π²ΠΏΠ»Π΅ΡΠ΅Π½ΠΈΡ" ΠΊΠΎΠ΄Π°
- ΠΡΠΏΡΠ°Π²Π»Π΅Π½Ρ ΠΎΡΠΈΠ±ΠΊΠΈ Π² Π°ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΡΡ ΠΌΠ΅ΡΠΎΠ΄Π°Ρ Ρ UniTask
- Π£Π»ΡΡΡΠ΅Π½Π° ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΡ Ρ UniTask
- ΠΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° UniTask
- ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Π°ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΡΡ ΡΡΠ΅ΠΉΡ-ΠΌΠ°ΡΠΈΠ½
- ΠΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π° ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° async/await
ΠΠ»Π°Π³ΠΎΠ΄Π°ΡΠ½ΠΎΡΡΠΈ...
ΠΡΠΎΡ ΠΏΠ»Π°Π³ΠΈΠ½ ΡΠ²Π»ΡΠ΅ΡΡΡ ΠΌΠΎΠ΄Π΅ΡΠ½ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠΉ Π²Π΅ΡΡΠΈΠ΅ΠΉ, ΠΎΡΠ½ΠΎΠ²Π°Π½Π½ΠΎΠΉ Π½Π° ΡΠ°Π±ΠΎΡΠ΅ ΡΠ»Π΅Π΄ΡΡΡΠΈΡ ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ²:
- MethodBoundaryAspect.Fody β ΠΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½Π°Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π΄Π»Ρ .NET, Π½Π° ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΎΡΠ½ΠΎΠ²Π°Π½ ΠΏΠ»Π°Π³ΠΈΠ½.
- Loxodon Framework β Π€ΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊ, ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²ΠΈΠ²ΡΠΈΠΉ ΠΏΠ΅ΡΠ²ΠΎΠ½Π°ΡΠ°Π»ΡΠ½ΡΡ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Fody Π² ΡΡΠ΅Π΄Ρ Unity.
ΠΠ»ΡΡΠ΅Π²ΠΎΠΉ Π²ΠΊΠ»Π°Π΄ ΡΡΠΎΠΉ Π²Π΅ΡΡΠΈΠΈ β Π³Π»ΡΠ±ΠΎΠΊΠ°Ρ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ ΠΈ Π½Π°Π΄Π΅ΠΆΠ½Π°Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ Π°ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΡΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ C#, ΡΠ°ΠΊΠΈΡ ΠΊΠ°ΠΊ UniTask.
