Repository: PowerShell/PowerShell
File: src/System.Management.Automation/engine/runtime/Binding/Binders.cs (C#)

Code before:
            if (ComInterop.ComBinder.TryBindInvokeMember(this, _propertySetter, target, args, out result))
            {
                return result.UpdateComRestrictionsForPsObject(args).WriteToDebugLog(this);
            }

            var targetValue = PSObject.Base(target.Value);
            if (targetValue == null)
            {
                if (!_static && !_nonEnumerating)
                {
                    // As discussed with Bruce, the Where/ForEach operators should work on $null and return an empty collection.
                    // e.g. $null.Where{"I didn't run"} should return an empty collection
                    DynamicMetaObject emptyEnumerator = new DynamicMetaObject(
                        Expression.Call(Expression.NewArrayInit(typeof(object)), CachedReflectionInfo.IEnumerable_GetEnumerator),
                        BindingRestrictions.GetInstanceRestriction(Expression.Call(CachedReflectionInfo.PSObject_Base, target.Expression), null))
                        .WriteToDebugLog(this);
                    BindingRestrictions argRestrictions = args.Aggregate(BindingRestrictions.Empty, static (current, arg) => current.Merge(arg.PSGetMethodArgumentRestriction()));

                    if (s_whereSearchValues.Contains(Name))
                    {
                        return InvokeWhereOnCollection(emptyEnumerator, args, argRestrictions).WriteToDebugLog(this);
                    }

                    if (s_foreachSearchValues.Contains(Name))
                        // We need to pass the empty enumerator to the ForEach operator, so that it can return an empty collection.
                        // The ForEach operator will not be able to call the script block if the enumerator is empty.
                    {
                        return InvokeForEachOnCollection(emptyEnumerator, args, argRestrictions).WriteToDebugLog(this);
                    }
                }

                return target.ThrowRuntimeError(args, BindingRestrictions.Empty, "InvokeMethodOnNull", ParserStrings.InvokeMethodOnNull).WriteToDebugLog(this);
            }

            PSMemberInfo memberInfo;
            if (_getMemberBinder.HasInstanceMember && PSGetMemberBinder.TryGetInstanceMember(target.Value, Name, out memberInfo))
            {
                // If there is an instance member, we generate (roughly) the following:
                //     PSMethodInfo methodInfo;
                //     if (PSInvokeMemberBinder.TryGetInstanceMethod(target.Value, Name, out methodInfoInfo))
                //         return methodInfo.Invoke(target, args);
                //     else
                //         update the site
                // We use a generic method like this because:
                //   * If one object has an instance property with a given name, it's like many others do as well
                //   * We want to avoid generating new sites for every object with an instance member
                // As an alternative, would could generate the following psuedo-code:
                //     if (target.Value == previousInstance)
                //         return optimized value (depending on the exact PSMemberInfo subclass)
                //     else update the site
                // But the assumption here is that many sites probably performs worse than the dictionary lookup

Review comment (refactor):
Does this apply to the `where` operator too? If so, can you move this comment to right above the `if (string.Equals(name, "where"))` line and also change the comment to call out `"Both 'ForEach' and 'Where' operator will not be able to ..."`

Code after:
            if (ComInterop.ComBinder.TryBindInvokeMember(this, _propertySetter, target, args, out result))
            {
                return result.UpdateComRestrictionsForPsObject(args).WriteToDebugLog(this);
            }

            var targetValue = PSObject.Base(target.Value);
            if (targetValue == null)
            {
                if (!_static && !_nonEnumerating)
                {
                    // As discussed with Bruce, the Where/ForEach operators should work on $null and return an empty collection.
                    // e.g. $null.Where{"I didn't run"} should return an empty collection
                    DynamicMetaObject emptyEnumerator = new DynamicMetaObject(
                        Expression.Call(Expression.NewArrayInit(typeof(object)), CachedReflectionInfo.IEnumerable_GetEnumerator),
                        BindingRestrictions.GetInstanceRestriction(Expression.Call(CachedReflectionInfo.PSObject_Base, target.Expression), null))
                        .WriteToDebugLog(this);
                    BindingRestrictions argRestrictions = args.Aggregate(BindingRestrictions.Empty, static (current, arg) => current.Merge(arg.PSGetMethodArgumentRestriction()));

                    // We need to pass the empty enumerator to the ForEach/Where operators, so that they can return an empty collection.
                    // The ForEach/Where operators will not be able to call the script block if the enumerator is empty.
                    if (s_whereSearchValues.Contains(Name))
                    {
                        return InvokeWhereOnCollection(emptyEnumerator, args, argRestrictions).WriteToDebugLog(this);
                    }

                    if (s_foreachSearchValues.Contains(Name))
                    {
                        return InvokeForEachOnCollection(emptyEnumerator, args, argRestrictions).WriteToDebugLog(this);
                    }
                }

                return target.ThrowRuntimeError(args, BindingRestrictions.Empty, "InvokeMethodOnNull", ParserStrings.InvokeMethodOnNull).WriteToDebugLog(this);
            }

            PSMemberInfo memberInfo;
            if (_getMemberBinder.HasInstanceMember && PSGetMemberBinder.TryGetInstanceMember(target.Value, Name, out memberInfo))
            {
                // If there is an instance member, we generate (roughly) the following:
                //     PSMethodInfo methodInfo;
                //     if (PSInvokeMemberBinder.TryGetInstanceMethod(target.Value, Name, out methodInfoInfo))
                //         return methodInfo.Invoke(target, args);
                //     else
                //         update the site
                // We use a generic method like this because:
                //   * If one object has an instance property with a given name, it's like many others do as well
                //   * We want to avoid generating new sites for every object with an instance member
                // As an alternative, would could generate the following psuedo-code:
                //     if (target.Value == previousInstance)
                //         return optimized value (depending on the exact PSMemberInfo subclass)
                //     else update the site
                // But the assumption here is that many sites probably performs worse than the dictionary lookup