We believe we need to add reflection emit to .NET Standard.
We already have features in .NET Standard that imply support for dynamic code generation, for example Assembly.Load(byte[]), RegexOptions.Compiled, and LambdaExpression.Compile().
Today, developers have no way to test whether runtime code generation is actually supported -- the runtime might throw PlatformNotSupported. Furthermore, even if it is supported, it might be slower as the runtime is interpreting the code (for example, by walking the expressions trees or interpreting the IL). While that ensures that scenarios that depend on code generation just work, it makes it hard to support scenarios that use code generation as a performance optimization. In those cases, not using code generation is usually faster.
Hence, we need an API that allows developers to check for the support of code generation as well as an API that allows developers to check using runtime code generation as a performance optimization is sensible.
API Proposal
namespace System.Runtime.CompilerServices
{
public static class RuntimeFeature
{
public static bool IsDynamicCodeSupported { get; }
public static bool IsDynamicCodeCompiled { get; }
}
}
The semantics are:
- If the runtime supports loading and running dynamic code,
IsDynamicCodeSupported returns true. This doesn't tell developers whether the code is interpreted or compiled.
- If the runtime supports loading and running dynamic code by using a just-in-time compiler,
IsDynamicCodeCompiled returns true.
RuntimeFeature.IsSupported(string) also returns the appropriate value when called with the strings "IsDynamicCodeSupported" and "IsDynamicCodeCompiled".
Affected features
- Loading assemblies from bytes (
Assembly.Load(byte[]))
- Linq expression trees
- Reflection emit
DynamicMethod (lightweight code generation, LCG)
- DispatchProxy
- Regex
Scenarios
Failing early with RuntimeFeature.IsDynamicCodeSupported
In some scenarios, performance isn't the primary goal but use to get stuff to work, for example, mocking. A mocking framework might provide an API like Proxy.Create<TInterface>() that returns you a type that implements TInterface. In this case, performance isn't the higher order and the only thing that matters is that the runtime supports generating code on the fly. Some environments, for example, iOS, disallow actual JITs. For that reason, the Xamarin team is considering providing an IL interpreter to make the scenarios work.
The author of the mocking framework could just use reflection emit and rely on the implementation of it to throw PlatformNotSupported but a much better developer experience would be to check early & fail early with a descriptive error message. For example:
public static class Proxy
{
public static Proxy<TInterface> Create<TInterface>()
{
if (!RuntimeFeature.IsDynamicCodeSupported)
throw new PlatformNotSupportedException("We cannot create proxies on your runtime because it doesn't allow dynamic code generation.");
return UseReflectionEmitToCreateImplementation();
}
}
Accelerating using RuntimeFeature.IsDynamicCodeCompiled
In most cases, dynamically generating code is meant as a performance optimization. This
var options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
// We only want to use compiled regexes if the underlying runtime
// supports compiling IL with a JIT. Otherwise it's likely much slower
// than walking the state machine.
if (RuntimeFeature.IsDynamicCodeCompiled)
{
options |= RegexOptions.Compiled;
}
Similar areas include using expression trees to accelerate setting properties and calling constructors in IoC systems.
We believe we need to add reflection emit to .NET Standard.
We already have features in .NET Standard that imply support for dynamic code generation, for example
Assembly.Load(byte[]),RegexOptions.Compiled, andLambdaExpression.Compile().Today, developers have no way to test whether runtime code generation is actually supported -- the runtime might throw
PlatformNotSupported. Furthermore, even if it is supported, it might be slower as the runtime is interpreting the code (for example, by walking the expressions trees or interpreting the IL). While that ensures that scenarios that depend on code generation just work, it makes it hard to support scenarios that use code generation as a performance optimization. In those cases, not using code generation is usually faster.Hence, we need an API that allows developers to check for the support of code generation as well as an API that allows developers to check using runtime code generation as a performance optimization is sensible.
API Proposal
The semantics are:
IsDynamicCodeSupportedreturnstrue. This doesn't tell developers whether the code is interpreted or compiled.IsDynamicCodeCompiledreturnstrue.RuntimeFeature.IsSupported(string)also returns the appropriate value when called with the strings"IsDynamicCodeSupported"and"IsDynamicCodeCompiled".Affected features
Assembly.Load(byte[]))DynamicMethod(lightweight code generation, LCG)Scenarios
Failing early with
RuntimeFeature.IsDynamicCodeSupportedIn some scenarios, performance isn't the primary goal but use to get stuff to work, for example, mocking. A mocking framework might provide an API like
Proxy.Create<TInterface>()that returns you a type that implementsTInterface. In this case, performance isn't the higher order and the only thing that matters is that the runtime supports generating code on the fly. Some environments, for example, iOS, disallow actual JITs. For that reason, the Xamarin team is considering providing an IL interpreter to make the scenarios work.The author of the mocking framework could just use reflection emit and rely on the implementation of it to throw
PlatformNotSupportedbut a much better developer experience would be to check early & fail early with a descriptive error message. For example:Accelerating using
RuntimeFeature.IsDynamicCodeCompiledIn most cases, dynamically generating code is meant as a performance optimization. This
Similar areas include using expression trees to accelerate setting properties and calling constructors in IoC systems.