Skip to content

Proposal for high-performance codegen-less Reflection factory APIs #23716

Description

@GrabYourPitchforks

/cc @jkotas

Background

There are certain scenarios today - largely involving activation, serialization, and DI - where library authors perform codegen in order to perform operations on arbitrary types. The primary reason for this is performance. The standard Reflection APIs are too slow to be used in the code paths targeted by these library authors, and though codegen has a large upfront cost it performs considerably better when amortized over the lifetime of the application.

This approach generally works well, but the .NET Framework is considering scenarios where it must operate in environments which do not allow codegen. This renders ineffective the existing performance improvement techniques used by these library authors.

We are uniquely positioned to provide a set of APIs which can cover the majority of scenarios traditionally involving reflection-based codegen. The general idea is that library authors can rely on the APIs we provide to work correctly both in codegen-enabled and in codegen-disallowed environments. Alternatively, the library authors can detect at runtime whether codegen is enabled, and if so they can use their existing highly-optimized codegen logic, falling back to the new API surface if codegen is disallowed.

Sample API surface

namespaceSystem.Reflection{publicdelegaterefTFieldFieldAccessor<TField>(objecttarget);publicdelegaterefTFieldFieldAccessor<TTarget,TField>(refTTargettarget);/// <summary>/// Provides factories that can be used by serializers, formatters, and DI systems/// to perform reflection-like activities in performance-critical code paths./// </summary>[SecurityCritical]publicstaticclassReflectionServices{publicstaticboolIsCodegenAllowed{get;}/* * FIELD ACCESSORS, GETTERS, AND SETTERS */publicstaticFieldAccessor<TField>CreateFieldAccessor<TField>(FieldInfofieldInfo);publicstaticFieldAccessor<TTarget,TField>CreateFieldAccessor<TTarget,TField>(FieldInfofieldInfo);publicstaticFunc<object,object>CreateFieldGetter(FieldInfofieldInfo);publicstaticFunc<object,TField>CreateFieldGetter<TField>(FieldInfofieldInfo);publicstaticFunc<TTarget,TField>CreateFieldGetter<TTarget,TField>(FieldInfofieldInfo);publicstaticAction<object,object>CreateFieldSetter(FieldInfofieldInfo);publicstaticAction<object,TField>CreateFieldSetter<TField>(FieldInfofieldInfo);// TTarget must not be value type; field must be an instance field.publicstaticAction<TTarget,TField>CreateFieldSetter<TTarget,TField>(FieldInfofieldInfo);/* * PARAMETERLESS OBJECT CREATION */publicstaticFunc<object>CreateInstanceFactory(Typetype);publicstaticFunc<T>CreateInstanceFactory<T>();/* * PARAMETERFUL OBJECT CREATION */publicstaticFunc<object[],object>CreateInstanceFactory(ConstructorInfoconstructorInfo);publicstaticFunc<object[],T>CreateInstanceFactory<T>(ConstructorInfoconstructorInfo);publicstaticDelegateCreateInstanceFactoryTyped(ConstructorInfoconstructorInfo,TypedelegateType);/* * PROPERTY GETTERS AND SETTERS * TODO: How would indexed properties be represented? Using the normal method invocation routines? */publicstaticFunc<object,object>CreatePropertyGetter(PropertyInfopropertyInfo);publicstaticFunc<object,TProperty>CreatePropertyGetter<TProperty>(PropertyInfopropertyInfo);publicstaticFunc<TTarget,TProperty>CreatePropertyGetter<TTarget,TProperty>(PropertyInfopropertyInfo);publicstaticAction<object,object>CreatePropertySetter(PropertyInfopropertyInfo);publicstaticAction<object,TProperty>CreatePropertySetter<TProperty>(PropertyInfopropertyInfo);// TTarget must not be value type; property must be an instance property.publicstaticAction<TTarget,TProperty>CreatePropertySetter<TTarget,TProperty>(PropertyInfopropertyInfo);/* * METHODS */publicstaticFunc<object,object[],object>CreateMethodInvoker(MethodInfomethodInfo);// If instance method, 'delegateType' must be open over 'this' parameter.publicstaticDelegateCreateMethodInvoker(MethodInfomethodInfo,TypedelegateType);/* * EVENTS */publicstaticAction<object,object>CreateEventSubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventSubscriber<TTarget,TDelegate>(EventInfoeventInfo);publicstaticAction<object,object>CreateEventUnsubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventUnsubscriber<TTarget,TDelegate>(EventInfoeventInfo);}}

Goals and non-goals

  • These APIs are not geared toward standard application developers who are already comfortable using the existing Reflection API surface. They are instead geared toward advanced library developers who need to perform Reflection operations in performance-sensitive code paths.

  • These APIs must work in a codegen-disallowed execution environment. (Are there exceptions?)

  • These APIs do not need to cover all scenarios currently allowed by the existing methods on MethodInfo and related types. For example, constructors that take ref or out parameters are sufficiently rare that we don't need to account for them. They can be invoked via the standard Reflection APIs.

  • These APIs do not need to have the same observable behavior as using the Reflection APIs; e.g., we may determine that these APIs should not throw TargetInvocationException on failure. But these APIs must provide consistent behavior regardless of whether they're running within a codegen-enabled or a codegen-disallowed environment.

  • Delegate creation does not need to be particularly optimized since there will be many checks performed upfront and we will ask callers to cache the returned delegate instances. However, once the delegates are created their invocation must be faster than calling the existing Reflection APIs. (Exception: if codegen is disallowed, then delegate invocation should be faster than calling the existing Reflection APIs wherever possible, and it must not be slower.)

  • It is an explicit goal to get serialization library authors to prefer this system over hand-rolling codegen for most member access scenarios. The selling points of this API would be ease of use (compared to hand-rolling codegen), performance, and the ability to work in a wide variety of execution environments.

  • It is an explicit non-goal to have performance characteristics equal to or better than a library's own custom codegen. For example, a DI system might choose to codegen a single method that both queries a service provider to get dependency instances and calls newobj on the target constructor. Such a system will always outperform these generalized APIs, but the API performance should be good enough that library authors would be generally satisfied using them over Reflection as a fallback in these scenarios.

  • These APIs do not need to support custom implementations of MemberInfo. Only support for CLR-backed members is required.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Reflectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsiderationtenet-performancePerformance related issue

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
     blocks
    (function() {
    function addCopyButtons() {
    document.querySelectorAll('pre code').forEach(function(codeBlock) {
    if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
    codeBlock.parentElement.setAttribute('data-copy-added', 'true');
    var btn = document.createElement('button');
    btn.textContent = 'Copy';
    btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
    btn.onmouseover = function() { this.style.opacity = '1'; };
    btn.onmouseout = function() { this.style.opacity = '0.7'; };
    btn.onclick = function() {
    navigator.clipboard.writeText(codeBlock.textContent).then(function() {
    btn.textContent = 'Copied!';
    setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
    });
    };
    codeBlock.parentElement.style.position = 'relative';
    codeBlock.parentElement.appendChild(btn);
    });
    }
    addCopyButtons();
    // Re-run on dynamic content
    var observer = new MutationObserver(addCopyButtons);
    observer.observe(document.body, { childList: true, subtree: true });
    })();
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    Proposal for high-performance codegen-less Reflection factory APIs · Issue #23716 · dotnet/runtime · GitHub
    Skip to content

    Proposal for high-performance codegen-less Reflection factory APIs #23716

    Description

    @GrabYourPitchforks

    /cc @jkotas

    Background

    There are certain scenarios today - largely involving activation, serialization, and DI - where library authors perform codegen in order to perform operations on arbitrary types. The primary reason for this is performance. The standard Reflection APIs are too slow to be used in the code paths targeted by these library authors, and though codegen has a large upfront cost it performs considerably better when amortized over the lifetime of the application.

    This approach generally works well, but the .NET Framework is considering scenarios where it must operate in environments which do not allow codegen. This renders ineffective the existing performance improvement techniques used by these library authors.

    We are uniquely positioned to provide a set of APIs which can cover the majority of scenarios traditionally involving reflection-based codegen. The general idea is that library authors can rely on the APIs we provide to work correctly both in codegen-enabled and in codegen-disallowed environments. Alternatively, the library authors can detect at runtime whether codegen is enabled, and if so they can use their existing highly-optimized codegen logic, falling back to the new API surface if codegen is disallowed.

    Sample API surface

    namespaceSystem.Reflection{publicdelegaterefTFieldFieldAccessor<TField>(objecttarget);publicdelegaterefTFieldFieldAccessor<TTarget,TField>(refTTargettarget);/// <summary>/// Provides factories that can be used by serializers, formatters, and DI systems/// to perform reflection-like activities in performance-critical code paths./// </summary>[SecurityCritical]publicstaticclassReflectionServices{publicstaticboolIsCodegenAllowed{get;}/* * FIELD ACCESSORS, GETTERS, AND SETTERS */publicstaticFieldAccessor<TField>CreateFieldAccessor<TField>(FieldInfofieldInfo);publicstaticFieldAccessor<TTarget,TField>CreateFieldAccessor<TTarget,TField>(FieldInfofieldInfo);publicstaticFunc<object,object>CreateFieldGetter(FieldInfofieldInfo);publicstaticFunc<object,TField>CreateFieldGetter<TField>(FieldInfofieldInfo);publicstaticFunc<TTarget,TField>CreateFieldGetter<TTarget,TField>(FieldInfofieldInfo);publicstaticAction<object,object>CreateFieldSetter(FieldInfofieldInfo);publicstaticAction<object,TField>CreateFieldSetter<TField>(FieldInfofieldInfo);// TTarget must not be value type; field must be an instance field.publicstaticAction<TTarget,TField>CreateFieldSetter<TTarget,TField>(FieldInfofieldInfo);/* * PARAMETERLESS OBJECT CREATION */publicstaticFunc<object>CreateInstanceFactory(Typetype);publicstaticFunc<T>CreateInstanceFactory<T>();/* * PARAMETERFUL OBJECT CREATION */publicstaticFunc<object[],object>CreateInstanceFactory(ConstructorInfoconstructorInfo);publicstaticFunc<object[],T>CreateInstanceFactory<T>(ConstructorInfoconstructorInfo);publicstaticDelegateCreateInstanceFactoryTyped(ConstructorInfoconstructorInfo,TypedelegateType);/* * PROPERTY GETTERS AND SETTERS * TODO: How would indexed properties be represented? Using the normal method invocation routines? */publicstaticFunc<object,object>CreatePropertyGetter(PropertyInfopropertyInfo);publicstaticFunc<object,TProperty>CreatePropertyGetter<TProperty>(PropertyInfopropertyInfo);publicstaticFunc<TTarget,TProperty>CreatePropertyGetter<TTarget,TProperty>(PropertyInfopropertyInfo);publicstaticAction<object,object>CreatePropertySetter(PropertyInfopropertyInfo);publicstaticAction<object,TProperty>CreatePropertySetter<TProperty>(PropertyInfopropertyInfo);// TTarget must not be value type; property must be an instance property.publicstaticAction<TTarget,TProperty>CreatePropertySetter<TTarget,TProperty>(PropertyInfopropertyInfo);/* * METHODS */publicstaticFunc<object,object[],object>CreateMethodInvoker(MethodInfomethodInfo);// If instance method, 'delegateType' must be open over 'this' parameter.publicstaticDelegateCreateMethodInvoker(MethodInfomethodInfo,TypedelegateType);/* * EVENTS */publicstaticAction<object,object>CreateEventSubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventSubscriber<TTarget,TDelegate>(EventInfoeventInfo);publicstaticAction<object,object>CreateEventUnsubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventUnsubscriber<TTarget,TDelegate>(EventInfoeventInfo);}}

    Goals and non-goals

    • These APIs are not geared toward standard application developers who are already comfortable using the existing Reflection API surface. They are instead geared toward advanced library developers who need to perform Reflection operations in performance-sensitive code paths.

    • These APIs must work in a codegen-disallowed execution environment. (Are there exceptions?)

    • These APIs do not need to cover all scenarios currently allowed by the existing methods on MethodInfo and related types. For example, constructors that take ref or out parameters are sufficiently rare that we don't need to account for them. They can be invoked via the standard Reflection APIs.

    • These APIs do not need to have the same observable behavior as using the Reflection APIs; e.g., we may determine that these APIs should not throw TargetInvocationException on failure. But these APIs must provide consistent behavior regardless of whether they're running within a codegen-enabled or a codegen-disallowed environment.

    • Delegate creation does not need to be particularly optimized since there will be many checks performed upfront and we will ask callers to cache the returned delegate instances. However, once the delegates are created their invocation must be faster than calling the existing Reflection APIs. (Exception: if codegen is disallowed, then delegate invocation should be faster than calling the existing Reflection APIs wherever possible, and it must not be slower.)

    • It is an explicit goal to get serialization library authors to prefer this system over hand-rolling codegen for most member access scenarios. The selling points of this API would be ease of use (compared to hand-rolling codegen), performance, and the ability to work in a wide variety of execution environments.

    • It is an explicit non-goal to have performance characteristics equal to or better than a library's own custom codegen. For example, a DI system might choose to codegen a single method that both queries a service provider to get dependency instances and calls newobj on the target constructor. Such a system will always outperform these generalized APIs, but the API performance should be good enough that library authors would be generally satisfied using them over Reflection as a fallback in these scenarios.

    • These APIs do not need to support custom implementations of MemberInfo. Only support for CLR-backed members is required.

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Reflectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsiderationtenet-performancePerformance related issue

      Type

      No type

      Projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Proposal for high-performance codegen-less Reflection factory APIs · Issue #23716 · dotnet/runtime · GitHub
      Skip to content

      Proposal for high-performance codegen-less Reflection factory APIs #23716

      Description

      @GrabYourPitchforks

      /cc @jkotas

      Background

      There are certain scenarios today - largely involving activation, serialization, and DI - where library authors perform codegen in order to perform operations on arbitrary types. The primary reason for this is performance. The standard Reflection APIs are too slow to be used in the code paths targeted by these library authors, and though codegen has a large upfront cost it performs considerably better when amortized over the lifetime of the application.

      This approach generally works well, but the .NET Framework is considering scenarios where it must operate in environments which do not allow codegen. This renders ineffective the existing performance improvement techniques used by these library authors.

      We are uniquely positioned to provide a set of APIs which can cover the majority of scenarios traditionally involving reflection-based codegen. The general idea is that library authors can rely on the APIs we provide to work correctly both in codegen-enabled and in codegen-disallowed environments. Alternatively, the library authors can detect at runtime whether codegen is enabled, and if so they can use their existing highly-optimized codegen logic, falling back to the new API surface if codegen is disallowed.

      Sample API surface

      namespaceSystem.Reflection{publicdelegaterefTFieldFieldAccessor<TField>(objecttarget);publicdelegaterefTFieldFieldAccessor<TTarget,TField>(refTTargettarget);/// <summary>/// Provides factories that can be used by serializers, formatters, and DI systems/// to perform reflection-like activities in performance-critical code paths./// </summary>[SecurityCritical]publicstaticclassReflectionServices{publicstaticboolIsCodegenAllowed{get;}/* * FIELD ACCESSORS, GETTERS, AND SETTERS */publicstaticFieldAccessor<TField>CreateFieldAccessor<TField>(FieldInfofieldInfo);publicstaticFieldAccessor<TTarget,TField>CreateFieldAccessor<TTarget,TField>(FieldInfofieldInfo);publicstaticFunc<object,object>CreateFieldGetter(FieldInfofieldInfo);publicstaticFunc<object,TField>CreateFieldGetter<TField>(FieldInfofieldInfo);publicstaticFunc<TTarget,TField>CreateFieldGetter<TTarget,TField>(FieldInfofieldInfo);publicstaticAction<object,object>CreateFieldSetter(FieldInfofieldInfo);publicstaticAction<object,TField>CreateFieldSetter<TField>(FieldInfofieldInfo);// TTarget must not be value type; field must be an instance field.publicstaticAction<TTarget,TField>CreateFieldSetter<TTarget,TField>(FieldInfofieldInfo);/* * PARAMETERLESS OBJECT CREATION */publicstaticFunc<object>CreateInstanceFactory(Typetype);publicstaticFunc<T>CreateInstanceFactory<T>();/* * PARAMETERFUL OBJECT CREATION */publicstaticFunc<object[],object>CreateInstanceFactory(ConstructorInfoconstructorInfo);publicstaticFunc<object[],T>CreateInstanceFactory<T>(ConstructorInfoconstructorInfo);publicstaticDelegateCreateInstanceFactoryTyped(ConstructorInfoconstructorInfo,TypedelegateType);/* * PROPERTY GETTERS AND SETTERS * TODO: How would indexed properties be represented? Using the normal method invocation routines? */publicstaticFunc<object,object>CreatePropertyGetter(PropertyInfopropertyInfo);publicstaticFunc<object,TProperty>CreatePropertyGetter<TProperty>(PropertyInfopropertyInfo);publicstaticFunc<TTarget,TProperty>CreatePropertyGetter<TTarget,TProperty>(PropertyInfopropertyInfo);publicstaticAction<object,object>CreatePropertySetter(PropertyInfopropertyInfo);publicstaticAction<object,TProperty>CreatePropertySetter<TProperty>(PropertyInfopropertyInfo);// TTarget must not be value type; property must be an instance property.publicstaticAction<TTarget,TProperty>CreatePropertySetter<TTarget,TProperty>(PropertyInfopropertyInfo);/* * METHODS */publicstaticFunc<object,object[],object>CreateMethodInvoker(MethodInfomethodInfo);// If instance method, 'delegateType' must be open over 'this' parameter.publicstaticDelegateCreateMethodInvoker(MethodInfomethodInfo,TypedelegateType);/* * EVENTS */publicstaticAction<object,object>CreateEventSubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventSubscriber<TTarget,TDelegate>(EventInfoeventInfo);publicstaticAction<object,object>CreateEventUnsubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventUnsubscriber<TTarget,TDelegate>(EventInfoeventInfo);}}

      Goals and non-goals

      • These APIs are not geared toward standard application developers who are already comfortable using the existing Reflection API surface. They are instead geared toward advanced library developers who need to perform Reflection operations in performance-sensitive code paths.

      • These APIs must work in a codegen-disallowed execution environment. (Are there exceptions?)

      • These APIs do not need to cover all scenarios currently allowed by the existing methods on MethodInfo and related types. For example, constructors that take ref or out parameters are sufficiently rare that we don't need to account for them. They can be invoked via the standard Reflection APIs.

      • These APIs do not need to have the same observable behavior as using the Reflection APIs; e.g., we may determine that these APIs should not throw TargetInvocationException on failure. But these APIs must provide consistent behavior regardless of whether they're running within a codegen-enabled or a codegen-disallowed environment.

      • Delegate creation does not need to be particularly optimized since there will be many checks performed upfront and we will ask callers to cache the returned delegate instances. However, once the delegates are created their invocation must be faster than calling the existing Reflection APIs. (Exception: if codegen is disallowed, then delegate invocation should be faster than calling the existing Reflection APIs wherever possible, and it must not be slower.)

      • It is an explicit goal to get serialization library authors to prefer this system over hand-rolling codegen for most member access scenarios. The selling points of this API would be ease of use (compared to hand-rolling codegen), performance, and the ability to work in a wide variety of execution environments.

      • It is an explicit non-goal to have performance characteristics equal to or better than a library's own custom codegen. For example, a DI system might choose to codegen a single method that both queries a service provider to get dependency instances and calls newobj on the target constructor. Such a system will always outperform these generalized APIs, but the API performance should be good enough that library authors would be generally satisfied using them over Reflection as a fallback in these scenarios.

      • These APIs do not need to support custom implementations of MemberInfo. Only support for CLR-backed members is required.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Reflectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsiderationtenet-performancePerformance related issue

        Type

        No type

        Projects

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Proposal for high-performance codegen-less Reflection factory APIs · Issue #23716 · dotnet/runtime · GitHub
        Skip to content

        Proposal for high-performance codegen-less Reflection factory APIs #23716

        Description

        @GrabYourPitchforks

        /cc @jkotas

        Background

        There are certain scenarios today - largely involving activation, serialization, and DI - where library authors perform codegen in order to perform operations on arbitrary types. The primary reason for this is performance. The standard Reflection APIs are too slow to be used in the code paths targeted by these library authors, and though codegen has a large upfront cost it performs considerably better when amortized over the lifetime of the application.

        This approach generally works well, but the .NET Framework is considering scenarios where it must operate in environments which do not allow codegen. This renders ineffective the existing performance improvement techniques used by these library authors.

        We are uniquely positioned to provide a set of APIs which can cover the majority of scenarios traditionally involving reflection-based codegen. The general idea is that library authors can rely on the APIs we provide to work correctly both in codegen-enabled and in codegen-disallowed environments. Alternatively, the library authors can detect at runtime whether codegen is enabled, and if so they can use their existing highly-optimized codegen logic, falling back to the new API surface if codegen is disallowed.

        Sample API surface

        namespaceSystem.Reflection{publicdelegaterefTFieldFieldAccessor<TField>(objecttarget);publicdelegaterefTFieldFieldAccessor<TTarget,TField>(refTTargettarget);/// <summary>/// Provides factories that can be used by serializers, formatters, and DI systems/// to perform reflection-like activities in performance-critical code paths./// </summary>[SecurityCritical]publicstaticclassReflectionServices{publicstaticboolIsCodegenAllowed{get;}/* * FIELD ACCESSORS, GETTERS, AND SETTERS */publicstaticFieldAccessor<TField>CreateFieldAccessor<TField>(FieldInfofieldInfo);publicstaticFieldAccessor<TTarget,TField>CreateFieldAccessor<TTarget,TField>(FieldInfofieldInfo);publicstaticFunc<object,object>CreateFieldGetter(FieldInfofieldInfo);publicstaticFunc<object,TField>CreateFieldGetter<TField>(FieldInfofieldInfo);publicstaticFunc<TTarget,TField>CreateFieldGetter<TTarget,TField>(FieldInfofieldInfo);publicstaticAction<object,object>CreateFieldSetter(FieldInfofieldInfo);publicstaticAction<object,TField>CreateFieldSetter<TField>(FieldInfofieldInfo);// TTarget must not be value type; field must be an instance field.publicstaticAction<TTarget,TField>CreateFieldSetter<TTarget,TField>(FieldInfofieldInfo);/* * PARAMETERLESS OBJECT CREATION */publicstaticFunc<object>CreateInstanceFactory(Typetype);publicstaticFunc<T>CreateInstanceFactory<T>();/* * PARAMETERFUL OBJECT CREATION */publicstaticFunc<object[],object>CreateInstanceFactory(ConstructorInfoconstructorInfo);publicstaticFunc<object[],T>CreateInstanceFactory<T>(ConstructorInfoconstructorInfo);publicstaticDelegateCreateInstanceFactoryTyped(ConstructorInfoconstructorInfo,TypedelegateType);/* * PROPERTY GETTERS AND SETTERS * TODO: How would indexed properties be represented? Using the normal method invocation routines? */publicstaticFunc<object,object>CreatePropertyGetter(PropertyInfopropertyInfo);publicstaticFunc<object,TProperty>CreatePropertyGetter<TProperty>(PropertyInfopropertyInfo);publicstaticFunc<TTarget,TProperty>CreatePropertyGetter<TTarget,TProperty>(PropertyInfopropertyInfo);publicstaticAction<object,object>CreatePropertySetter(PropertyInfopropertyInfo);publicstaticAction<object,TProperty>CreatePropertySetter<TProperty>(PropertyInfopropertyInfo);// TTarget must not be value type; property must be an instance property.publicstaticAction<TTarget,TProperty>CreatePropertySetter<TTarget,TProperty>(PropertyInfopropertyInfo);/* * METHODS */publicstaticFunc<object,object[],object>CreateMethodInvoker(MethodInfomethodInfo);// If instance method, 'delegateType' must be open over 'this' parameter.publicstaticDelegateCreateMethodInvoker(MethodInfomethodInfo,TypedelegateType);/* * EVENTS */publicstaticAction<object,object>CreateEventSubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventSubscriber<TTarget,TDelegate>(EventInfoeventInfo);publicstaticAction<object,object>CreateEventUnsubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventUnsubscriber<TTarget,TDelegate>(EventInfoeventInfo);}}

        Goals and non-goals

        • These APIs are not geared toward standard application developers who are already comfortable using the existing Reflection API surface. They are instead geared toward advanced library developers who need to perform Reflection operations in performance-sensitive code paths.

        • These APIs must work in a codegen-disallowed execution environment. (Are there exceptions?)

        • These APIs do not need to cover all scenarios currently allowed by the existing methods on MethodInfo and related types. For example, constructors that take ref or out parameters are sufficiently rare that we don't need to account for them. They can be invoked via the standard Reflection APIs.

        • These APIs do not need to have the same observable behavior as using the Reflection APIs; e.g., we may determine that these APIs should not throw TargetInvocationException on failure. But these APIs must provide consistent behavior regardless of whether they're running within a codegen-enabled or a codegen-disallowed environment.

        • Delegate creation does not need to be particularly optimized since there will be many checks performed upfront and we will ask callers to cache the returned delegate instances. However, once the delegates are created their invocation must be faster than calling the existing Reflection APIs. (Exception: if codegen is disallowed, then delegate invocation should be faster than calling the existing Reflection APIs wherever possible, and it must not be slower.)

        • It is an explicit goal to get serialization library authors to prefer this system over hand-rolling codegen for most member access scenarios. The selling points of this API would be ease of use (compared to hand-rolling codegen), performance, and the ability to work in a wide variety of execution environments.

        • It is an explicit non-goal to have performance characteristics equal to or better than a library's own custom codegen. For example, a DI system might choose to codegen a single method that both queries a service provider to get dependency instances and calls newobj on the target constructor. Such a system will always outperform these generalized APIs, but the API performance should be good enough that library authors would be generally satisfied using them over Reflection as a fallback in these scenarios.

        • These APIs do not need to support custom implementations of MemberInfo. Only support for CLR-backed members is required.

        Metadata

        Metadata

        Assignees

        No one assigned

          Labels

          api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Reflectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsiderationtenet-performancePerformance related issue

          Type

          No type

          Projects

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Proposal for high-performance codegen-less Reflection factory APIs · Issue #23716 · dotnet/runtime · GitHub
          Skip to content

          Proposal for high-performance codegen-less Reflection factory APIs #23716

          Description

          @GrabYourPitchforks

          /cc @jkotas

          Background

          There are certain scenarios today - largely involving activation, serialization, and DI - where library authors perform codegen in order to perform operations on arbitrary types. The primary reason for this is performance. The standard Reflection APIs are too slow to be used in the code paths targeted by these library authors, and though codegen has a large upfront cost it performs considerably better when amortized over the lifetime of the application.

          This approach generally works well, but the .NET Framework is considering scenarios where it must operate in environments which do not allow codegen. This renders ineffective the existing performance improvement techniques used by these library authors.

          We are uniquely positioned to provide a set of APIs which can cover the majority of scenarios traditionally involving reflection-based codegen. The general idea is that library authors can rely on the APIs we provide to work correctly both in codegen-enabled and in codegen-disallowed environments. Alternatively, the library authors can detect at runtime whether codegen is enabled, and if so they can use their existing highly-optimized codegen logic, falling back to the new API surface if codegen is disallowed.

          Sample API surface

          namespaceSystem.Reflection{publicdelegaterefTFieldFieldAccessor<TField>(objecttarget);publicdelegaterefTFieldFieldAccessor<TTarget,TField>(refTTargettarget);/// <summary>/// Provides factories that can be used by serializers, formatters, and DI systems/// to perform reflection-like activities in performance-critical code paths./// </summary>[SecurityCritical]publicstaticclassReflectionServices{publicstaticboolIsCodegenAllowed{get;}/* * FIELD ACCESSORS, GETTERS, AND SETTERS */publicstaticFieldAccessor<TField>CreateFieldAccessor<TField>(FieldInfofieldInfo);publicstaticFieldAccessor<TTarget,TField>CreateFieldAccessor<TTarget,TField>(FieldInfofieldInfo);publicstaticFunc<object,object>CreateFieldGetter(FieldInfofieldInfo);publicstaticFunc<object,TField>CreateFieldGetter<TField>(FieldInfofieldInfo);publicstaticFunc<TTarget,TField>CreateFieldGetter<TTarget,TField>(FieldInfofieldInfo);publicstaticAction<object,object>CreateFieldSetter(FieldInfofieldInfo);publicstaticAction<object,TField>CreateFieldSetter<TField>(FieldInfofieldInfo);// TTarget must not be value type; field must be an instance field.publicstaticAction<TTarget,TField>CreateFieldSetter<TTarget,TField>(FieldInfofieldInfo);/* * PARAMETERLESS OBJECT CREATION */publicstaticFunc<object>CreateInstanceFactory(Typetype);publicstaticFunc<T>CreateInstanceFactory<T>();/* * PARAMETERFUL OBJECT CREATION */publicstaticFunc<object[],object>CreateInstanceFactory(ConstructorInfoconstructorInfo);publicstaticFunc<object[],T>CreateInstanceFactory<T>(ConstructorInfoconstructorInfo);publicstaticDelegateCreateInstanceFactoryTyped(ConstructorInfoconstructorInfo,TypedelegateType);/* * PROPERTY GETTERS AND SETTERS * TODO: How would indexed properties be represented? Using the normal method invocation routines? */publicstaticFunc<object,object>CreatePropertyGetter(PropertyInfopropertyInfo);publicstaticFunc<object,TProperty>CreatePropertyGetter<TProperty>(PropertyInfopropertyInfo);publicstaticFunc<TTarget,TProperty>CreatePropertyGetter<TTarget,TProperty>(PropertyInfopropertyInfo);publicstaticAction<object,object>CreatePropertySetter(PropertyInfopropertyInfo);publicstaticAction<object,TProperty>CreatePropertySetter<TProperty>(PropertyInfopropertyInfo);// TTarget must not be value type; property must be an instance property.publicstaticAction<TTarget,TProperty>CreatePropertySetter<TTarget,TProperty>(PropertyInfopropertyInfo);/* * METHODS */publicstaticFunc<object,object[],object>CreateMethodInvoker(MethodInfomethodInfo);// If instance method, 'delegateType' must be open over 'this' parameter.publicstaticDelegateCreateMethodInvoker(MethodInfomethodInfo,TypedelegateType);/* * EVENTS */publicstaticAction<object,object>CreateEventSubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventSubscriber<TTarget,TDelegate>(EventInfoeventInfo);publicstaticAction<object,object>CreateEventUnsubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventUnsubscriber<TTarget,TDelegate>(EventInfoeventInfo);}}

          Goals and non-goals

          • These APIs are not geared toward standard application developers who are already comfortable using the existing Reflection API surface. They are instead geared toward advanced library developers who need to perform Reflection operations in performance-sensitive code paths.

          • These APIs must work in a codegen-disallowed execution environment. (Are there exceptions?)

          • These APIs do not need to cover all scenarios currently allowed by the existing methods on MethodInfo and related types. For example, constructors that take ref or out parameters are sufficiently rare that we don't need to account for them. They can be invoked via the standard Reflection APIs.

          • These APIs do not need to have the same observable behavior as using the Reflection APIs; e.g., we may determine that these APIs should not throw TargetInvocationException on failure. But these APIs must provide consistent behavior regardless of whether they're running within a codegen-enabled or a codegen-disallowed environment.

          • Delegate creation does not need to be particularly optimized since there will be many checks performed upfront and we will ask callers to cache the returned delegate instances. However, once the delegates are created their invocation must be faster than calling the existing Reflection APIs. (Exception: if codegen is disallowed, then delegate invocation should be faster than calling the existing Reflection APIs wherever possible, and it must not be slower.)

          • It is an explicit goal to get serialization library authors to prefer this system over hand-rolling codegen for most member access scenarios. The selling points of this API would be ease of use (compared to hand-rolling codegen), performance, and the ability to work in a wide variety of execution environments.

          • It is an explicit non-goal to have performance characteristics equal to or better than a library's own custom codegen. For example, a DI system might choose to codegen a single method that both queries a service provider to get dependency instances and calls newobj on the target constructor. Such a system will always outperform these generalized APIs, but the API performance should be good enough that library authors would be generally satisfied using them over Reflection as a fallback in these scenarios.

          • These APIs do not need to support custom implementations of MemberInfo. Only support for CLR-backed members is required.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Reflectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsiderationtenet-performancePerformance related issue

            Type

            No type

            Projects

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Proposal for high-performance codegen-less Reflection factory APIs · Issue #23716 · dotnet/runtime · GitHub
            Skip to content

            Proposal for high-performance codegen-less Reflection factory APIs #23716

            Description

            @GrabYourPitchforks

            /cc @jkotas

            Background

            There are certain scenarios today - largely involving activation, serialization, and DI - where library authors perform codegen in order to perform operations on arbitrary types. The primary reason for this is performance. The standard Reflection APIs are too slow to be used in the code paths targeted by these library authors, and though codegen has a large upfront cost it performs considerably better when amortized over the lifetime of the application.

            This approach generally works well, but the .NET Framework is considering scenarios where it must operate in environments which do not allow codegen. This renders ineffective the existing performance improvement techniques used by these library authors.

            We are uniquely positioned to provide a set of APIs which can cover the majority of scenarios traditionally involving reflection-based codegen. The general idea is that library authors can rely on the APIs we provide to work correctly both in codegen-enabled and in codegen-disallowed environments. Alternatively, the library authors can detect at runtime whether codegen is enabled, and if so they can use their existing highly-optimized codegen logic, falling back to the new API surface if codegen is disallowed.

            Sample API surface

            namespaceSystem.Reflection{publicdelegaterefTFieldFieldAccessor<TField>(objecttarget);publicdelegaterefTFieldFieldAccessor<TTarget,TField>(refTTargettarget);/// <summary>/// Provides factories that can be used by serializers, formatters, and DI systems/// to perform reflection-like activities in performance-critical code paths./// </summary>[SecurityCritical]publicstaticclassReflectionServices{publicstaticboolIsCodegenAllowed{get;}/* * FIELD ACCESSORS, GETTERS, AND SETTERS */publicstaticFieldAccessor<TField>CreateFieldAccessor<TField>(FieldInfofieldInfo);publicstaticFieldAccessor<TTarget,TField>CreateFieldAccessor<TTarget,TField>(FieldInfofieldInfo);publicstaticFunc<object,object>CreateFieldGetter(FieldInfofieldInfo);publicstaticFunc<object,TField>CreateFieldGetter<TField>(FieldInfofieldInfo);publicstaticFunc<TTarget,TField>CreateFieldGetter<TTarget,TField>(FieldInfofieldInfo);publicstaticAction<object,object>CreateFieldSetter(FieldInfofieldInfo);publicstaticAction<object,TField>CreateFieldSetter<TField>(FieldInfofieldInfo);// TTarget must not be value type; field must be an instance field.publicstaticAction<TTarget,TField>CreateFieldSetter<TTarget,TField>(FieldInfofieldInfo);/* * PARAMETERLESS OBJECT CREATION */publicstaticFunc<object>CreateInstanceFactory(Typetype);publicstaticFunc<T>CreateInstanceFactory<T>();/* * PARAMETERFUL OBJECT CREATION */publicstaticFunc<object[],object>CreateInstanceFactory(ConstructorInfoconstructorInfo);publicstaticFunc<object[],T>CreateInstanceFactory<T>(ConstructorInfoconstructorInfo);publicstaticDelegateCreateInstanceFactoryTyped(ConstructorInfoconstructorInfo,TypedelegateType);/* * PROPERTY GETTERS AND SETTERS * TODO: How would indexed properties be represented? Using the normal method invocation routines? */publicstaticFunc<object,object>CreatePropertyGetter(PropertyInfopropertyInfo);publicstaticFunc<object,TProperty>CreatePropertyGetter<TProperty>(PropertyInfopropertyInfo);publicstaticFunc<TTarget,TProperty>CreatePropertyGetter<TTarget,TProperty>(PropertyInfopropertyInfo);publicstaticAction<object,object>CreatePropertySetter(PropertyInfopropertyInfo);publicstaticAction<object,TProperty>CreatePropertySetter<TProperty>(PropertyInfopropertyInfo);// TTarget must not be value type; property must be an instance property.publicstaticAction<TTarget,TProperty>CreatePropertySetter<TTarget,TProperty>(PropertyInfopropertyInfo);/* * METHODS */publicstaticFunc<object,object[],object>CreateMethodInvoker(MethodInfomethodInfo);// If instance method, 'delegateType' must be open over 'this' parameter.publicstaticDelegateCreateMethodInvoker(MethodInfomethodInfo,TypedelegateType);/* * EVENTS */publicstaticAction<object,object>CreateEventSubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventSubscriber<TTarget,TDelegate>(EventInfoeventInfo);publicstaticAction<object,object>CreateEventUnsubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventUnsubscriber<TTarget,TDelegate>(EventInfoeventInfo);}}

            Goals and non-goals

            • These APIs are not geared toward standard application developers who are already comfortable using the existing Reflection API surface. They are instead geared toward advanced library developers who need to perform Reflection operations in performance-sensitive code paths.

            • These APIs must work in a codegen-disallowed execution environment. (Are there exceptions?)

            • These APIs do not need to cover all scenarios currently allowed by the existing methods on MethodInfo and related types. For example, constructors that take ref or out parameters are sufficiently rare that we don't need to account for them. They can be invoked via the standard Reflection APIs.

            • These APIs do not need to have the same observable behavior as using the Reflection APIs; e.g., we may determine that these APIs should not throw TargetInvocationException on failure. But these APIs must provide consistent behavior regardless of whether they're running within a codegen-enabled or a codegen-disallowed environment.

            • Delegate creation does not need to be particularly optimized since there will be many checks performed upfront and we will ask callers to cache the returned delegate instances. However, once the delegates are created their invocation must be faster than calling the existing Reflection APIs. (Exception: if codegen is disallowed, then delegate invocation should be faster than calling the existing Reflection APIs wherever possible, and it must not be slower.)

            • It is an explicit goal to get serialization library authors to prefer this system over hand-rolling codegen for most member access scenarios. The selling points of this API would be ease of use (compared to hand-rolling codegen), performance, and the ability to work in a wide variety of execution environments.

            • It is an explicit non-goal to have performance characteristics equal to or better than a library's own custom codegen. For example, a DI system might choose to codegen a single method that both queries a service provider to get dependency instances and calls newobj on the target constructor. Such a system will always outperform these generalized APIs, but the API performance should be good enough that library authors would be generally satisfied using them over Reflection as a fallback in these scenarios.

            • These APIs do not need to support custom implementations of MemberInfo. Only support for CLR-backed members is required.

            Metadata

            Metadata

            Assignees

            No one assigned

              Labels

              api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Reflectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsiderationtenet-performancePerformance related issue

              Type

              No type

              Projects

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Proposal for high-performance codegen-less Reflection factory APIs · Issue #23716 · dotnet/runtime · GitHub
              Skip to content

              Proposal for high-performance codegen-less Reflection factory APIs #23716

              Description

              @GrabYourPitchforks

              /cc @jkotas

              Background

              There are certain scenarios today - largely involving activation, serialization, and DI - where library authors perform codegen in order to perform operations on arbitrary types. The primary reason for this is performance. The standard Reflection APIs are too slow to be used in the code paths targeted by these library authors, and though codegen has a large upfront cost it performs considerably better when amortized over the lifetime of the application.

              This approach generally works well, but the .NET Framework is considering scenarios where it must operate in environments which do not allow codegen. This renders ineffective the existing performance improvement techniques used by these library authors.

              We are uniquely positioned to provide a set of APIs which can cover the majority of scenarios traditionally involving reflection-based codegen. The general idea is that library authors can rely on the APIs we provide to work correctly both in codegen-enabled and in codegen-disallowed environments. Alternatively, the library authors can detect at runtime whether codegen is enabled, and if so they can use their existing highly-optimized codegen logic, falling back to the new API surface if codegen is disallowed.

              Sample API surface

              namespaceSystem.Reflection{publicdelegaterefTFieldFieldAccessor<TField>(objecttarget);publicdelegaterefTFieldFieldAccessor<TTarget,TField>(refTTargettarget);/// <summary>/// Provides factories that can be used by serializers, formatters, and DI systems/// to perform reflection-like activities in performance-critical code paths./// </summary>[SecurityCritical]publicstaticclassReflectionServices{publicstaticboolIsCodegenAllowed{get;}/* * FIELD ACCESSORS, GETTERS, AND SETTERS */publicstaticFieldAccessor<TField>CreateFieldAccessor<TField>(FieldInfofieldInfo);publicstaticFieldAccessor<TTarget,TField>CreateFieldAccessor<TTarget,TField>(FieldInfofieldInfo);publicstaticFunc<object,object>CreateFieldGetter(FieldInfofieldInfo);publicstaticFunc<object,TField>CreateFieldGetter<TField>(FieldInfofieldInfo);publicstaticFunc<TTarget,TField>CreateFieldGetter<TTarget,TField>(FieldInfofieldInfo);publicstaticAction<object,object>CreateFieldSetter(FieldInfofieldInfo);publicstaticAction<object,TField>CreateFieldSetter<TField>(FieldInfofieldInfo);// TTarget must not be value type; field must be an instance field.publicstaticAction<TTarget,TField>CreateFieldSetter<TTarget,TField>(FieldInfofieldInfo);/* * PARAMETERLESS OBJECT CREATION */publicstaticFunc<object>CreateInstanceFactory(Typetype);publicstaticFunc<T>CreateInstanceFactory<T>();/* * PARAMETERFUL OBJECT CREATION */publicstaticFunc<object[],object>CreateInstanceFactory(ConstructorInfoconstructorInfo);publicstaticFunc<object[],T>CreateInstanceFactory<T>(ConstructorInfoconstructorInfo);publicstaticDelegateCreateInstanceFactoryTyped(ConstructorInfoconstructorInfo,TypedelegateType);/* * PROPERTY GETTERS AND SETTERS * TODO: How would indexed properties be represented? Using the normal method invocation routines? */publicstaticFunc<object,object>CreatePropertyGetter(PropertyInfopropertyInfo);publicstaticFunc<object,TProperty>CreatePropertyGetter<TProperty>(PropertyInfopropertyInfo);publicstaticFunc<TTarget,TProperty>CreatePropertyGetter<TTarget,TProperty>(PropertyInfopropertyInfo);publicstaticAction<object,object>CreatePropertySetter(PropertyInfopropertyInfo);publicstaticAction<object,TProperty>CreatePropertySetter<TProperty>(PropertyInfopropertyInfo);// TTarget must not be value type; property must be an instance property.publicstaticAction<TTarget,TProperty>CreatePropertySetter<TTarget,TProperty>(PropertyInfopropertyInfo);/* * METHODS */publicstaticFunc<object,object[],object>CreateMethodInvoker(MethodInfomethodInfo);// If instance method, 'delegateType' must be open over 'this' parameter.publicstaticDelegateCreateMethodInvoker(MethodInfomethodInfo,TypedelegateType);/* * EVENTS */publicstaticAction<object,object>CreateEventSubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventSubscriber<TTarget,TDelegate>(EventInfoeventInfo);publicstaticAction<object,object>CreateEventUnsubscriber(EventInfoeventInfo);// Event must be an instance event.publicstaticAction<TTarget,TDelegate>CreateEventUnsubscriber<TTarget,TDelegate>(EventInfoeventInfo);}}

              Goals and non-goals

              • These APIs are not geared toward standard application developers who are already comfortable using the existing Reflection API surface. They are instead geared toward advanced library developers who need to perform Reflection operations in performance-sensitive code paths.

              • These APIs must work in a codegen-disallowed execution environment. (Are there exceptions?)

              • These APIs do not need to cover all scenarios currently allowed by the existing methods on MethodInfo and related types. For example, constructors that take ref or out parameters are sufficiently rare that we don't need to account for them. They can be invoked via the standard Reflection APIs.

              • These APIs do not need to have the same observable behavior as using the Reflection APIs; e.g., we may determine that these APIs should not throw TargetInvocationException on failure. But these APIs must provide consistent behavior regardless of whether they're running within a codegen-enabled or a codegen-disallowed environment.

              • Delegate creation does not need to be particularly optimized since there will be many checks performed upfront and we will ask callers to cache the returned delegate instances. However, once the delegates are created their invocation must be faster than calling the existing Reflection APIs. (Exception: if codegen is disallowed, then delegate invocation should be faster than calling the existing Reflection APIs wherever possible, and it must not be slower.)

              • It is an explicit goal to get serialization library authors to prefer this system over hand-rolling codegen for most member access scenarios. The selling points of this API would be ease of use (compared to hand-rolling codegen), performance, and the ability to work in a wide variety of execution environments.

              • It is an explicit non-goal to have performance characteristics equal to or better than a library's own custom codegen. For example, a DI system might choose to codegen a single method that both queries a service provider to get dependency instances and calls newobj on the target constructor. Such a system will always outperform these generalized APIs, but the API performance should be good enough that library authors would be generally satisfied using them over Reflection as a fallback in these scenarios.

              • These APIs do not need to support custom implementations of MemberInfo. Only support for CLR-backed members is required.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Reflectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsiderationtenet-performancePerformance related issue

                Type

                No type

                Projects

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions