Project "Caravela" 0.3 / / Caravela Documentation / Conceptual Documentation / Creating Aspects / Creating Simple Aspects / Overriding Methods

Overriding Methods

The simplest and most common aspect is to wrap the hand-written body of a method with some automatically-generated code, but without modifying the method body itself.

You can achieve this thanks to the OverrideMethodAspect abstract class. OverrideMethodAspect is the aspect-oriented implementation of the decorator design pattern for methods.

Creating an OverrideMethod aspect

  1. Create a new class derived from the OverrideMethodAspect abstract class. This class will be a custom attribute, so it is a good idea to name it with the Attribute suffix.

  2. Implement the OverrideMethod() method in plain C#. This method will serve as a template defining the way the aspect overrides the hand-written target method.

    • To insert code or expressions that depend on the target method of the aspect (such as the method name or the parameter type), use the meta API.
    • Where the original implementation must be invoked, call the meta.Proceed method.
  3. The aspect is a custom attribute. To transform a method using the aspect, just add the aspect custom attribute to the method.

Example: an empty OverrideMethod aspect

The following code shows an empty OverrideMethodAspect, which does not do anything:

            using Caravela.Framework.Aspects;

namespace Caravela.Documentation.SampleCode.AspectFramework
{
    public class EmptyOverrideMethodAttribute : OverrideMethodAspect
    {
        public override dynamic OverrideMethod()
        {
            return meta.Proceed();
        }
    }
}

          

Accessing the metadata and parameters of the overridden method

The metadata of the method being overridden are available from the template method on the meta.Target.Method property . This property gives you all information about the name, type, parameters and custom attributes of the method. For instance, the metadata of method parameters are exposed on meta.Target.Method.Parameters. But note that only metadata are exposed there.

To access the parameter values, you need to access meta.Target.Parameters. For instance:

  • meta.Target.Parameters[0].Value gives you the value of the first parameter,
  • meta.Target.Parameters.Values.ToArray() creates an object[] array with all parameter values,
  • meta.Target.Parameters["a"].Value = 5 sets the a parameter to 5.

Example: simple logging

The following code writes a message to the system console before and after the method execution. The text includes the name of the target method.

                using System;
using Caravela.Framework.Aspects;

namespace Caravela.Documentation.SampleCode.AspectFramework.SimpleLogging
{
    public class SimpleLogAttribute : OverrideMethodAspect
    {
        public override dynamic OverrideMethod()
        {
            Console.WriteLine($"Entering {meta.Target.Method.ToDisplayString()}");

            try
            {
                return meta.Proceed();
            }
            finally
            {
                Console.WriteLine($"Leaving {meta.Target.Method.ToDisplayString()}");
            }
        }
    }


}

              
                
using System;

namespace Caravela.Documentation.SampleCode.AspectFramework.SimpleLogging
{
    internal class TargetCode
    {
        [SimpleLog]
        public void Method1()
        {
            Console.WriteLine("Hello, world.");
        }
    }
}


              
                using System;

namespace Caravela.Documentation.SampleCode.AspectFramework.SimpleLogging
{
    internal class TargetCode
    {
        [SimpleLog]
        public void Method1()
        {
            Console.WriteLine($"Entering TargetCode.Method1()");
            try
            {
                Console.WriteLine("Hello, world.");
                return;
            }
            finally
            {
                Console.WriteLine($"Leaving TargetCode.Method1()");
            }
        }
    }
}
              

Invoking the method with different arguments

When you call meta.Proceed, the aspect framework generates a call to the overridden method and passes the parameters it received. If the parameter value has been changed thanks to a statement like meta.Target.Parameters["a"].Value = 5, the modified value will be passed.

If you want to invoke the method with a totally different set of arguments, you can do it using meta.Target.Method.Invoke.

Note

Invoking a method with ref or out parameters is not yet supported.

Overriding async and iterator methods

By default, the OverrideMethod() method is used as a template for all methods, including async and iterator methods.

To make the default template work naturally even for async and iterator methods, calls to meta.Proceed() and return statements are interpreted differently in each situation in order to respect the intent of normal (non-async, non-iterator) code. That is, the default behavior tries to respect the decorator pattern.

Warning

Applying the default OverrideMethod() template to an iterator makes the stream to be buffered into a List<T>. In case of long-running streams, this buffering may be undesirable. In this case, specific iterator templates must be specified (see below).

The following table lists the transformations applied to the meta.Proceed() expression and the return statement when a default template is applied to an async or iterator method:

Target Method Transformation of "meta.Proceed()" Transformation of "return result;"
async await meta.Proceed() return result
IEnumerable<T> iterator RunTimeAspectHelper.Buffer( meta.Proceed() ) returning a List<T> return result
IEnumerator<T> iterator RunTimeAspectHelper.Buffer( meta.Proceed() ) returning a List<T>.Enumerator return result
IAsyncEnumerable<T> async await RunTimeAspectHelper.BufferAsync( meta.Proceed() ) returning an AsyncEnumerableList<T> await foreach (var r in result) { yield return r; } yield break;
IAsyncEnumerator<T> async await RunTimeAspectHelper.BufferAsync( meta.Proceed() ) returning an AsyncEnumerableList<T>.AsyncEnumerator await using ( result ) { while (await result.MoveNextAsync()) { yield return result.Current; } } yield break;

As you can see, the buffering of iterators is performed by the Buffer and BufferAsync methods.

Example: the default template applied to all kinds of methods

The following example demonstrates the behavior of the default template when applied to different kinds of methods. Note that the output of iterators methods is buffered. This is visible in the program output.

                using System;
using Caravela.Framework.Aspects;

namespace Caravela.Documentation.SampleCode.AspectFramework.OverrideMethodDefaultTemplateAllKinds
{
    public class LogAttribute : OverrideMethodAspect
    {
        public override dynamic OverrideMethod()
        {
            Console.WriteLine($"{meta.Target.Method.Name}: start");
            var result = meta.Proceed();
            Console.WriteLine($"{meta.Target.Method.Name}: returning {result}.");
            return result;
        }
    }
}

              
                using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Caravela.Documentation.SampleCode.AspectFramework.OverrideMethodDefaultTemplateAllKinds
{

    public class Program
    {
        [Log]
        public static int NormalMethod()
        {
            return 5;
        }

        [Log]
        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("  Task.Yield");
            await Task.Yield();
            Console.WriteLine("  Resuming");
            return 5;
        }

        [Log]
        public static IEnumerable<int> EnumerableMethod()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static IEnumerator<int> EnumeratorMethod()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerable<int> AsyncEnumerableMethod()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerator<int> AsyncEnumeratorMethod()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        public static async Task Main()
        {
            NormalMethod();

            await AsyncMethod();

            foreach (var a in EnumerableMethod())
            {
                Console.WriteLine($" Received {a} from EnumerableMethod");
            }
            Console.WriteLine("---");

            var enumerator = EnumeratorMethod();
            while (enumerator.MoveNext())
            {
                Console.WriteLine($" Received {enumerator.Current} from EnumeratorMethod");
            }

            Console.WriteLine("---");
            await foreach (var a in AsyncEnumerableMethod())
            {
                Console.WriteLine($" Received {a} from AsyncEnumerableMethod");
            }

            Console.WriteLine("---");
            var asyncEnumerator = AsyncEnumeratorMethod();
            while (await asyncEnumerator.MoveNextAsync())
            {
                Console.WriteLine($" Received {asyncEnumerator.Current} from AsyncEnumeratorMethod");
            }
        }
    }
}

              
                using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Caravela.Framework.RunTime;

namespace Caravela.Documentation.SampleCode.AspectFramework.OverrideMethodDefaultTemplateAllKinds
{

    public class Program
    {
        [Log]
        public static int NormalMethod()
        {
            Console.WriteLine($"NormalMethod: start");
            int result;
            result = 5;
            goto __aspect_return_1;
        __aspect_return_1:
            Console.WriteLine($"NormalMethod: returning {result}.");
            return result;
        }

        [Log]
        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine($"AsyncMethod: start");
            var result = await AsyncMethod_Source();
            Console.WriteLine($"AsyncMethod: returning {result}.");
            return result;
        }

        private static async Task<int> AsyncMethod_Source()
        {
            Console.WriteLine("  Task.Yield");
            await Task.Yield();
            Console.WriteLine("  Resuming");
            return 5;
        }

        [Log]
        public static IEnumerable<int> EnumerableMethod()
        {
            Console.WriteLine($"EnumerableMethod: start");
            var result = EnumerableMethod_Source().Buffer();
            Console.WriteLine($"EnumerableMethod: returning {result}.");
            return result;
        }

        private static IEnumerable<int> EnumerableMethod_Source()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static IEnumerator<int> EnumeratorMethod()
        {
            Console.WriteLine($"EnumeratorMethod: start");
            var result = EnumeratorMethod_Source().Buffer();
            Console.WriteLine($"EnumeratorMethod: returning {result}.");
            return result;
        }

        private static IEnumerator<int> EnumeratorMethod_Source()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerable<int> AsyncEnumerableMethod()
        {
            Console.WriteLine($"AsyncEnumerableMethod: start");
            var result = await AsyncEnumerableMethod_Source().BufferAsync();
            Console.WriteLine($"AsyncEnumerableMethod: returning {result}.");
            await foreach (var r in result)
            {
                yield return r;
            }

            yield break;
        }

        private static async IAsyncEnumerable<int> AsyncEnumerableMethod_Source()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerator<int> AsyncEnumeratorMethod()
        {
            Console.WriteLine($"AsyncEnumeratorMethod: start");
            var result = await AsyncEnumeratorMethod_Source().BufferAsync();
            Console.WriteLine($"AsyncEnumeratorMethod: returning {result}.");
            await using (result)
            {
                while (await result.MoveNextAsync())
                {
                    yield return result.Current;
                }
            }

            yield break;
        }

        private static async IAsyncEnumerator<int> AsyncEnumeratorMethod_Source()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        public static async Task Main()
        {
            NormalMethod();

            await AsyncMethod();

            foreach (var a in EnumerableMethod())
            {
                Console.WriteLine($" Received {a} from EnumerableMethod");
            }
            Console.WriteLine("---");

            var enumerator = EnumeratorMethod();
            while (enumerator.MoveNext())
            {
                Console.WriteLine($" Received {enumerator.Current} from EnumeratorMethod");
            }

            Console.WriteLine("---");
            await foreach (var a in AsyncEnumerableMethod())
            {
                Console.WriteLine($" Received {a} from AsyncEnumerableMethod");
            }

            Console.WriteLine("---");
            var asyncEnumerator = AsyncEnumeratorMethod();
            while (await asyncEnumerator.MoveNextAsync())
            {
                Console.WriteLine($" Received {asyncEnumerator.Current} from AsyncEnumeratorMethod");
            }
        }
    }
}
              
NormalMethod: start
NormalMethod: returning 5.
AsyncMethod: start
  Task.Yield
  Resuming
AsyncMethod: returning 5.
EnumerableMethod: start
  Yielding 1
  Yielding 2
  Yielding 3
EnumerableMethod: returning System.Collections.Generic.List`1[System.Int32].
 Received 1 from EnumerableMethod
 Received 2 from EnumerableMethod
 Received 3 from EnumerableMethod
---
EnumeratorMethod: start
  Yielding 1
  Yielding 2
  Yielding 3
EnumeratorMethod: returning System.Collections.Generic.List`1+Enumerator[System.Int32].
 Received 1 from EnumeratorMethod
 Received 2 from EnumeratorMethod
 Received 3 from EnumeratorMethod
---
AsyncEnumerableMethod: start
  Yielding 1
  Yielding 2
  Yielding 3
AsyncEnumerableMethod: returning Caravela.Framework.RunTime.AsyncEnumerableList`1[System.Int32].
 Received 1 from AsyncEnumerableMethod
 Received 2 from AsyncEnumerableMethod
 Received 3 from AsyncEnumerableMethod
---
AsyncEnumeratorMethod: start
  Yielding 1
  Yielding 2
  Yielding 3
AsyncEnumeratorMethod: returning Caravela.Framework.RunTime.AsyncEnumerableList`1+AsyncEnumerator[System.Int32].
 Received 1 from AsyncEnumeratorMethod
 Received 2 from AsyncEnumeratorMethod
 Received 3 from AsyncEnumeratorMethod

Implementing a specific template

The default template works great most of the time even on async methods and iterators, but it has a few limitations:

  • You cannot use await or yield in the default template.
  • When you call meta.Proceed() in the default template, it causes the complete evaluation of the async method or iterator.

To overcome these limitations, you can implement different variants of the OverrideMethod. For each variant, instead of calling meta.Proceed, you will call a variant of this method that has a relevant return type.

Template Method Proceed Method Description
OverrideAsyncMethod() ProceedAsync() Applies to all async methods, including async iterators, except if a more specific template is implemented.
OverrideEnumerableMethod() ProceedEnumerable() Applies to iterator methods returning an IEnumerable<T> or IEnumerable.
OverrideEnumeratorMethod() ProceedEnumerator() Applies to iterator methods returning an IEnumerator<T> or IEnumerator.
OverrideAsyncEnumerableMethod() ProceedAsyncEnumerable() Applies to async iterator methods returning an IAsyncEnumerable<T>.
OverrideAsyncEnumeratorMethod() ProceedAsyncEnumerator() Applies to async iterator methods returning an IAsyncEnumerator<T>.

Note that there is no obligation to implement these methods as async methods or yield-based iterators.

Example: specific templates for all kinds of methods

The following example derives from the previous one implements all specific template methods instead of just the default template methods. Note that now the output of iterators is no longer buffered, because this new version of the aspect supports iterator streaming.

                using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Caravela.Framework.Aspects;

namespace Caravela.Documentation.SampleCode.AspectFramework.OverrideMethodSpecificTemplateAllKinds
{
    public class LogAttribute : OverrideMethodAspect
    {
        public override dynamic OverrideMethod()
        {
            Console.WriteLine($"{meta.Target.Method.Name}: start");
            var result = meta.Proceed();
            Console.WriteLine($"{meta.Target.Method.Name}: returning {result}.");
            return result;
        }

        public override async Task<dynamic?> OverrideAsyncMethod()
        {
            Console.WriteLine($"{meta.Target.Method.Name}: start");
            var result = await meta.ProceedAsync();
            Console.WriteLine($"{meta.Target.Method.Name}: returning {result}.");
            return result;
        }

        public override IEnumerable<dynamic?> OverrideEnumerableMethod()
        {
            Console.WriteLine($"{meta.Target.Method.Name}: start");
            foreach (var item in meta.ProceedEnumerable())
            {
                Console.WriteLine($"{meta.Target.Method.Name}: intercepted {item}.");
                yield return item;
            }
            Console.WriteLine($"{meta.Target.Method.Name}: completed.");
        }


        public override IEnumerator<dynamic?> OverrideEnumeratorMethod()
        {
            Console.WriteLine($"{meta.Target.Method.Name}: start");
            using (var enumerator = meta.ProceedEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    Console.WriteLine($"{meta.Target.Method.Name}: intercepted {enumerator.Current}.");
                    yield return enumerator.Current;
                }
            }
            Console.WriteLine($"{meta.Target.Method.Name}: completed.");
        }


        public override async IAsyncEnumerable<dynamic?> OverrideAsyncEnumerableMethod()
        {
            Console.WriteLine($"{meta.Target.Method.Name}: start");
            await foreach (var item in meta.ProceedAsyncEnumerable())
            {
                Console.WriteLine($"{meta.Target.Method.Name}: intercepted {item}.");
                yield return item;
            }
            Console.WriteLine($"{meta.Target.Method.Name}: completed.");
        }

        public override async IAsyncEnumerator<dynamic?> OverrideAsyncEnumeratorMethod()
        {
            Console.WriteLine($"{meta.Target.Method.Name}: start");
            await using (var enumerator = meta.ProceedAsyncEnumerator())
            {
                while (await enumerator.MoveNextAsync())
                {
                    Console.WriteLine($"{meta.Target.Method.Name}: intercepted {enumerator.Current}.");
                    yield return enumerator.Current;
                }
            }
            Console.WriteLine($"{meta.Target.Method.Name}: completed.");

        }
    }
}

              
                using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Caravela.Documentation.SampleCode.AspectFramework.OverrideMethodSpecificTemplateAllKinds
{

    public class Program
    {

        [Log]
        public static int NormalMethod()
        {
            return 5;
        }

        [Log]
        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("  Task.Yield");
            await Task.Yield();
            Console.WriteLine("  Resuming");
            return 5;
        }

        [Log]
        public static IEnumerable<int> EnumerableMethod()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static IEnumerator<int> EnumeratorMethod()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerable<int> AsyncEnumerableMethod()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerator<int> AsyncEnumeratorMethod()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        public static async Task Main()
        {
            NormalMethod();

            await AsyncMethod();

            foreach (var a in EnumerableMethod())
            {
                Console.WriteLine($" Received {a} from EnumerableMethod");
            }
            Console.WriteLine("---");

            var enumerator = EnumeratorMethod();
            while (enumerator.MoveNext())
            {
                Console.WriteLine($" Received {enumerator.Current} from EnumeratorMethod");
            }
            Console.WriteLine("---");

            await foreach (var a in AsyncEnumerableMethod())
            {
                Console.WriteLine($" Received {a} from AsyncEnumerableMethod");
            }
            Console.WriteLine("---");

            var asyncEnumerator = AsyncEnumeratorMethod();
            while (await asyncEnumerator.MoveNextAsync())
            {
                Console.WriteLine($" Received {asyncEnumerator.Current} from AsyncEnumeratorMethod");
            }
        }
    }
}

              
                using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Caravela.Documentation.SampleCode.AspectFramework.OverrideMethodSpecificTemplateAllKinds
{

    public class Program
    {

        [Log]
        public static int NormalMethod()
        {
            Console.WriteLine($"NormalMethod: start");
            int result;
            result = 5;
            goto __aspect_return_1;
        __aspect_return_1:
            Console.WriteLine($"NormalMethod: returning {result}.");
            return result;
        }

        [Log]
        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine($"AsyncMethod: start");
            var result = await Program.AsyncMethod_Source();
            Console.WriteLine($"AsyncMethod: returning {result}.");
            return result;
        }

        private static async Task<int> AsyncMethod_Source()
        {
            Console.WriteLine("  Task.Yield");
            await Task.Yield();
            Console.WriteLine("  Resuming");
            return 5;
        }

        [Log]
        public static IEnumerable<int> EnumerableMethod()
        {
            Console.WriteLine($"EnumerableMethod: start");
            foreach (var item in Program.EnumerableMethod_Source())
            {
                Console.WriteLine($"EnumerableMethod: intercepted {item}.");
                yield return item;
            }

            Console.WriteLine($"EnumerableMethod: completed.");
        }

        private static IEnumerable<int> EnumerableMethod_Source()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static IEnumerator<int> EnumeratorMethod()
        {
            Console.WriteLine($"EnumeratorMethod: start");
            using (var enumerator = Program.EnumeratorMethod_Source())
            {
                while (enumerator.MoveNext())
                {
                    Console.WriteLine($"EnumeratorMethod: intercepted {enumerator.Current}.");
                    yield return enumerator.Current;
                }
            }

            Console.WriteLine($"EnumeratorMethod: completed.");
        }

        private static IEnumerator<int> EnumeratorMethod_Source()
        {
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerable<int> AsyncEnumerableMethod()
        {
            Console.WriteLine($"AsyncEnumerableMethod: start");
            await foreach (var item in Program.AsyncEnumerableMethod_Source())
            {
                Console.WriteLine($"AsyncEnumerableMethod: intercepted {item}.");
                yield return item;
            }

            Console.WriteLine($"AsyncEnumerableMethod: completed.");
        }

        private static async IAsyncEnumerable<int> AsyncEnumerableMethod_Source()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        [Log]
        public static async IAsyncEnumerator<int> AsyncEnumeratorMethod()
        {
            Console.WriteLine($"AsyncEnumeratorMethod: start");
            await using (var enumerator = Program.AsyncEnumeratorMethod_Source())
            {
                while (await enumerator.MoveNextAsync())
                {
                    Console.WriteLine($"AsyncEnumeratorMethod: intercepted {enumerator.Current}.");
                    yield return enumerator.Current;
                }
            }

            Console.WriteLine($"AsyncEnumeratorMethod: completed.");
        }

        private static async IAsyncEnumerator<int> AsyncEnumeratorMethod_Source()
        {
            await Task.Yield();
            Console.WriteLine("  Yielding 1");
            yield return 1;
            Console.WriteLine("  Yielding 2");
            yield return 2;
            Console.WriteLine("  Yielding 3");
            yield return 3;
        }

        public static async Task Main()
        {
            NormalMethod();

            await AsyncMethod();

            foreach (var a in EnumerableMethod())
            {
                Console.WriteLine($" Received {a} from EnumerableMethod");
            }
            Console.WriteLine("---");

            var enumerator = EnumeratorMethod();
            while (enumerator.MoveNext())
            {
                Console.WriteLine($" Received {enumerator.Current} from EnumeratorMethod");
            }
            Console.WriteLine("---");

            await foreach (var a in AsyncEnumerableMethod())
            {
                Console.WriteLine($" Received {a} from AsyncEnumerableMethod");
            }
            Console.WriteLine("---");

            var asyncEnumerator = AsyncEnumeratorMethod();
            while (await asyncEnumerator.MoveNextAsync())
            {
                Console.WriteLine($" Received {asyncEnumerator.Current} from AsyncEnumeratorMethod");
            }
        }
    }
}
              
NormalMethod: start
NormalMethod: returning 5.
AsyncMethod: start
  Task.Yield
  Resuming
AsyncMethod: returning 5.
EnumerableMethod: start
  Yielding 1
EnumerableMethod: intercepted 1.
 Received 1 from EnumerableMethod
  Yielding 2
EnumerableMethod: intercepted 2.
 Received 2 from EnumerableMethod
  Yielding 3
EnumerableMethod: intercepted 3.
 Received 3 from EnumerableMethod
EnumerableMethod: completed.
---
EnumeratorMethod: start
  Yielding 1
EnumeratorMethod: intercepted 1.
 Received 1 from EnumeratorMethod
  Yielding 2
EnumeratorMethod: intercepted 2.
 Received 2 from EnumeratorMethod
  Yielding 3
EnumeratorMethod: intercepted 3.
 Received 3 from EnumeratorMethod
EnumeratorMethod: completed.
---
AsyncEnumerableMethod: start
  Yielding 1
AsyncEnumerableMethod: intercepted 1.
 Received 1 from AsyncEnumerableMethod
  Yielding 2
AsyncEnumerableMethod: intercepted 2.
 Received 2 from AsyncEnumerableMethod
  Yielding 3
AsyncEnumerableMethod: intercepted 3.
 Received 3 from AsyncEnumerableMethod
AsyncEnumerableMethod: completed.
---
AsyncEnumeratorMethod: start
  Yielding 1
AsyncEnumeratorMethod: intercepted 1.
 Received 1 from AsyncEnumeratorMethod
  Yielding 2
AsyncEnumeratorMethod: intercepted 2.
 Received 2 from AsyncEnumeratorMethod
  Yielding 3
AsyncEnumeratorMethod: intercepted 3.
 Received 3 from AsyncEnumeratorMethod
AsyncEnumeratorMethod: completed.

Using specific templates for non-async awaitable or non-yield enumerable methods

If you want to use the specific templates for methods that have the correct return type but are not implemented using await or yield, set the UseAsyncTemplateForAnyAwaitable or UseEnumerableTemplateForAnyEnumerable property of the OverrideMethodAspect class to true in the aspect constructor.