Skip to content

Commit 88cf8f4

Browse files
authored
[Blazor] Compiler runtime APIs needed for bind:get, bind:set, bind:after (#40143)
Adds additional runtime helper APIs needed for @Bind:get, @Bind:set, @Bind:after
1 parent 5834b3e commit 88cf8f4

File tree

4 files changed

+1136
-88
lines changed

4 files changed

+1136
-88
lines changed

src/Components/Components/src/CompilerServices/RuntimeHelpers.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Runtime.CompilerServices;
5+
46
namespace Microsoft.AspNetCore.Components.CompilerServices;
57

68
/// <summary>
@@ -50,4 +52,76 @@ public static EventCallback<T> CreateInferredEventCallback<T>(object receiver, F
5052
{
5153
return EventCallback.Factory.Create<T>(receiver, callback);
5254
}
55+
56+
/// <summary>
57+
/// Not intended for use by application code.
58+
/// </summary>
59+
/// <param name="receiver"></param>
60+
/// <param name="callback"></param>
61+
/// <param name="value"></param>
62+
/// <returns></returns>
63+
//
64+
// This method is used with `@bind-Value` for components. When a component has a generic type, it's
65+
// really messy to write the parameter type for ValueChanged - because it can contain generic
66+
// type parameters. We're using a trick of type inference to generate the proper typing for the delegate
67+
// so that method-group-to-delegate conversion works.
68+
public static EventCallback<T> CreateInferredEventCallback<T>(object receiver, EventCallback<T> callback, T value)
69+
{
70+
return EventCallback.Factory.Create<T>(receiver, callback);
71+
}
72+
73+
/// <summary>
74+
/// Not intended for use by application code.
75+
/// </summary>
76+
/// <param name="callback"></param>
77+
/// <returns></returns>
78+
//
79+
// This method is used with `@bind-Value:after` for components. When :after is provided we don't know the
80+
// type of the expression provided by the developer or if we can invoke it directly, as it can be a lambda
81+
// and unlike in JavaScript, C# doesn't support Immediately Invoked Function Expressions so we need to pass
82+
// the expression to this helper method and invoke it inside.
83+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
84+
public static void InvokeSynchronousDelegate(Action callback)
85+
{
86+
callback();
87+
}
88+
89+
/// <summary>
90+
/// Not intended for use by application code.
91+
/// </summary>
92+
/// <param name="callback"></param>
93+
/// <returns></returns>
94+
//
95+
// This method is used with `@bind-Value:after` for components. When :after is provided we don't know the
96+
// type of the expression provided by the developer or if we can invoke it directly, as it can be a lambda
97+
// and unlike in JavaScript, C# doesn't support Immediately Invoked Function Expressions so we need to pass
98+
// the expression to this helper method and invoke it inside.
99+
// In addition to that, when the receiving target delegate property result is awaitable, we can receive either
100+
// an Action or a Func<Task> and we don't have that information at compile time, so we use this helper to
101+
// normalize both operations into a Task in the same way we do for EventCallback
102+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103+
public static Task InvokeAsynchronousDelegate(Action callback)
104+
{
105+
callback();
106+
return Task.CompletedTask;
107+
}
108+
109+
/// <summary>
110+
/// Not intended for use by application code.
111+
/// </summary>
112+
/// <param name="callback"></param>
113+
/// <returns></returns>
114+
//
115+
// This method is used with `@bind-Value:after` for components. When :after is provided we don't know the
116+
// type of the expression provided by the developer or if we can invoke it directly, as it can be a lambda
117+
// and unlike in JavaScript, C# doesn't support Immediately Invoked Function Expressions so we need to pass
118+
// the expression to this helper method and invoke it inside.
119+
// In addition to that, when the receiving target delegate property result is awaitable, we can receive either
120+
// an Action or a Func<Task> and we don't have that information at compile time, so we use this helper to
121+
// normalize both operations into a Task in the same way we do for EventCallback
122+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
123+
public static Task InvokeAsynchronousDelegate(Func<Task> callback)
124+
{
125+
return callback();
126+
}
53127
}

0 commit comments

Comments
 (0)