5

Hello guys i am trying to create my own Task-Like types using the AsyncMethodBuilderAttribute for the .NetCore framework.
So far in .Net Core my custom Type works and it can be awaited and i can return it as async [MyTypeName]<T> MyMethod() instead of Task .
However in .NetStandard 2.0 the attribute is not present and and i tried to implement it:
I decorated my awaitable type with the attribute but it still does not let me use it: "The return type of an async method must be void,Task,Task<T>"

So far i can use something like this in .Net Core and it works !!

public async Task WrapperMethod(){
   int result=await GetIntAsync();
}
public async Errand<int> GetIntAsync()
{
  await Task.Delay(1000);
  return 3;
}

Attribute:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)]
    public sealed class AsyncMethodBuilderAttribute : Attribute {
        //
        // Parameters:
        //   builderType:
        public AsyncMethodBuilderAttribute(Type builderType) {
            this.BuilderType = builderType;
        }

        //
        public Type BuilderType { get; }
    }


Below are the implementation of the custom type and the async method builder.The awaiter is not important in this case but i can provide the source if needed.

My Task-Like Type

[AsyncMethodBuilder(typeof(Builder))]
    public partial class Errand {

        private readonly Builder mbuilder;
        public Errand() {
        }
        internal Errand(Builder methodBuilder) => this.mbuilder = methodBuilder;

        private readonly HashSet<Awaiter> awaiters = new HashSet<Awaiter>();

        protected bool isCompleted = false;

        public Awaiter GetAwaiter() {

            Awaiter result = new Awaiter(this);
            this.awaiters.Add(result);
            return result;
        }

    }

The custom AsyncMethodBuilder implementation:

public partial class Builder {

            #region " Compiler integration "

            public static Builder Create() {
                return new Builder();
            }

            protected IAsyncStateMachine myStateMachine;

            public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {
                this.myStateMachine = stateMachine;
                this.errand = new Errand(this);
                stateMachine.MoveNext();
            }


            public void SetException(Exception ex) {

            }

            public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine machine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine {

                var foo = machine as IAsyncStateMachine;
                awaiter.OnCompleted(() => { foo.MoveNext(); });
            }
            public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine machine)where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine {

                IAsyncStateMachine asyncMachine = machine as IAsyncStateMachine;
                awaiter.OnCompleted(() => asyncMachine.MoveNext());

            }

            private Errand errand;
            public Errand Task { get { return this.errand; } }


            public void SetStateMachine(IAsyncStateMachine stateMachine) {

            }
            public void SetResult() {

            }

            #endregion


            //  internal void AddAwaiter(Awaiter awaiter) => this.awaiters.Add(awaiter);

        }

What else should i add for it to work in .Net Standard?

8
  • 1
    @dasblinkenlight: Yes you can. It's like ExtensionAttribute - the compiler expects to find the attribute in the right namespace, but will use it from wherever it can find it. I wouldn't recommend it (preferring to use a nuget package), but it's doable. My guess is that the OP isn't using a C# 7 compiler, but it's quite hard to tell.
    – Jon Skeet
    Commented Apr 27, 2018 at 10:17
  • 1
    Note that the attribute must be declared in the System.Runtime.CompilerServices namespace - we can't tell from the code posted whether that's where it's actually declared.
    – Jon Skeet
    Commented Apr 27, 2018 at 10:18
  • 1
    @DaisyShipton that's actually the correct answer - OP puts it in wrong namespace.
    – Evk
    Commented Apr 27, 2018 at 10:44
  • @Evk: Well it may be... but we can't tell for sure. I'm not sure I want to add an answer on a hunch.
    – Jon Skeet
    Commented Apr 27, 2018 at 11:06
  • 1
    @BercoviciAdrian but can you put your attribute in right namespace and tell us if that resolved the problem or not?
    – Evk
    Commented Apr 27, 2018 at 11:13

1 Answer 1

4

It doesn't matter where the source file is for the attribute, but it does need to have the right namespace declaration:

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)]
    public sealed class AsyncMethodBuilderAttribute : Attribute
    {
        public AsyncMethodBuilderAttribute(Type builderType) =>
            BuilderType = builderType;
        public Type BuilderType { get; }
    }
}
1
  • Ooooh you are right :D Sorry i was thinking i have to somehow put it in the source of the SDK. Commented Apr 27, 2018 at 11:19

Not the answer you're looking for? Browse other questions tagged or ask your own question.