File: System\Xaml\Schema\XamlMemberInvoker.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Xaml\System.Xaml.csproj (System.Xaml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Reflection;
using System.Xaml.MS.Impl;
 
namespace System.Xaml.Schema
{
    public class XamlMemberInvoker
    {
        private static XamlMemberInvoker s_Directive;
        private static XamlMemberInvoker s_Unknown;
        private static object[] s_emptyObjectArray = Array.Empty<object>();
 
        private XamlMember _member;
        private NullableReference<MethodInfo> _shouldSerializeMethod;
 
        protected XamlMemberInvoker()
        {
        }
 
        public XamlMemberInvoker(XamlMember member)
        {
            _member = member ?? throw new ArgumentNullException(nameof(member));
        }
 
        public static XamlMemberInvoker UnknownInvoker
        {
            get
            {
                if (s_Unknown is null)
                {
                    s_Unknown = new XamlMemberInvoker();
                }
                return s_Unknown;
            }
        }
 
        public MethodInfo UnderlyingGetter
        {
            get { return IsUnknown ? null : _member.Getter; }
        }
 
        public MethodInfo UnderlyingSetter
        {
            get { return IsUnknown ? null : _member.Setter; }
        }
 
        public virtual object GetValue(object instance)
        {
            ArgumentNullException.ThrowIfNull(instance);
            ThrowIfUnknown();
            if (UnderlyingGetter is null)
            {
                throw new NotSupportedException(SR.Format(SR.CantGetWriteonlyProperty, _member));
            }
 
            if (UnderlyingGetter.IsStatic)
            {
                return UnderlyingGetter.Invoke(null, new object[] { instance });
            }
            else
            {
                return UnderlyingGetter.Invoke(instance, s_emptyObjectArray);
            }
        }
 
        public virtual void SetValue(object instance, object value)
        {
            ArgumentNullException.ThrowIfNull(instance);
            ThrowIfUnknown();
            if (UnderlyingSetter is null)
            {
                throw new NotSupportedException(SR.Format(SR.CantSetReadonlyProperty, _member));
            }
 
            if (UnderlyingSetter.IsStatic)
            {
                UnderlyingSetter.Invoke(null, new object[] { instance, value });
            }
            else
            {
                UnderlyingSetter.Invoke(instance, new object[] { value });
            }
        }
 
        internal static XamlMemberInvoker DirectiveInvoker
        {
            get
            {
                if (s_Directive is null)
                {
                    s_Directive = new DirectiveMemberInvoker();
                }
                return s_Directive;
            }
        }
 
        // Returns true/false if ShouldSerialize method was invoked, null if no method was found
        public virtual ShouldSerializeResult ShouldSerializeValue(object instance)
        {
            if (IsUnknown)
            {
                return ShouldSerializeResult.Default;
            }
 
            // Look up the ShouldSerializeMethod
            if (!_shouldSerializeMethod.IsSet)
            {
                Type declaringType = _member.UnderlyingMember.DeclaringType;
                string methodName = KnownStrings.ShouldSerialize + _member.Name;
                BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
                Type[] args;
                if (_member.IsAttachable)
                {
                    args = new Type[] { _member.TargetType.UnderlyingType ?? typeof(object) };
                }
                else
                {
                    flags |= BindingFlags.Instance;
                    args = Type.EmptyTypes;
                }
                _shouldSerializeMethod.Value = declaringType.GetMethod(methodName, flags, null, args, null);
            }
 
            // Invoke the method if we found one
            MethodInfo shouldSerializeMethod = _shouldSerializeMethod.Value;
            if (shouldSerializeMethod is not null)
            {
                bool result;
                if (_member.IsAttachable)
                {
                    result = (bool)shouldSerializeMethod.Invoke(null, new object[] { instance });
                }
                else
                {
                    result = (bool)shouldSerializeMethod.Invoke(instance, null);
                }
 
                return result ? ShouldSerializeResult.True : ShouldSerializeResult.False;
            }
            return ShouldSerializeResult.Default;
        }
 
        private bool IsUnknown
        {
            get { return _member is null || _member.UnderlyingMember is null; }
        }
 
        private void ThrowIfUnknown()
        {
            if (IsUnknown)
            {
                throw new NotSupportedException(SR.NotSupportedOnUnknownMember);
            }
        }
 
        private class DirectiveMemberInvoker : XamlMemberInvoker
        {
            public override object GetValue(object instance)
            {
                throw new NotSupportedException(SR.NotSupportedOnDirective);
            }
 
            public override void SetValue(object instance, object value)
            {
                throw new NotSupportedException(SR.NotSupportedOnDirective);
            }
        }
    }
 
    public enum ShouldSerializeResult
    {
        Default,
        True,
        False
    }
}