예제 #1
0
        static List <IMember> DetectMemberOrder(ITypeDefinition recordTypeDef, Dictionary <IField, IProperty> backingFieldToAutoProperty)
        {
            // For records, the order of members is important:
            // Equals/GetHashCode/PrintMembers must agree on an order of fields+properties.
            // The IL metadata has the order of fields and the order of properties, but we
            // need to detect the correct interleaving.
            // We could try to detect this from the PrintMembers body, but let's initially
            // restrict ourselves to the common case where the record only uses properties.
            var subst = recordTypeDef.AsParameterizedType().GetSubstitution();

            return(recordTypeDef.Properties.Select(p => p.Specialize(subst)).Concat(
                       recordTypeDef.Fields.Select(f => (IField)f.Specialize(subst)).Where(f => !backingFieldToAutoProperty.ContainsKey(f))
                       ).ToList());
        }
예제 #2
0
        void DetectAutomaticProperties()
        {
            var subst = recordTypeDef.AsParameterizedType().GetSubstitution();

            foreach (var property in recordTypeDef.Properties)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var p = (IProperty)property.Specialize(subst);
                if (IsAutoProperty(p, out var field))
                {
                    backingFieldToAutoProperty.Add(field, p);
                    autoPropertyToBackingField.Add(p, field);
                }
            }

            bool IsAutoProperty(IProperty p, out IField field)
            {
                field = null;
                if (p.Parameters.Count != 0)
                {
                    return(false);
                }
                if (p.Getter != null)
                {
                    if (!IsAutoGetter(p.Getter, out field))
                    {
                        return(false);
                    }
                }
                if (p.Setter != null)
                {
                    if (!IsAutoSetter(p.Setter, out var field2))
                    {
                        return(false);
                    }
                    if (field != null)
                    {
                        if (!field.Equals(field2))
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        field = field2;
                    }
                }
                if (field == null)
                {
                    return(false);
                }
                if (!IsRecordType(field.DeclaringType))
                {
                    return(false);
                }
                return(field.Name == $"<{p.Name}>k__BackingField");
            }

            bool IsAutoGetter(IMethod method, out IField field)
            {
                field = null;
                var body = DecompileBody(method);

                if (body == null)
                {
                    return(false);
                }
                // return this.field;
                if (!body.Instructions[0].MatchReturn(out var retVal))
                {
                    return(false);
                }
                if (method.IsStatic)
                {
                    return(retVal.MatchLdsFld(out field));
                }
                else
                {
                    if (!retVal.MatchLdFld(out var target, out field))
                    {
                        return(false);
                    }
                    return(target.MatchLdThis());
                }
            }

            bool IsAutoSetter(IMethod method, out IField field)
            {
                field = null;
                Debug.Assert(!method.IsStatic);
                var body = DecompileBody(method);

                if (body == null)
                {
                    return(false);
                }
                // this.field = value;
                ILInstruction valueInst;

                if (method.IsStatic)
                {
                    if (!body.Instructions[0].MatchStsFld(out field, out valueInst))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!body.Instructions[0].MatchStFld(out var target, out field, out valueInst))
                    {
                        return(false);
                    }
                    if (!target.MatchLdThis())
                    {
                        return(false);
                    }
                }
                if (!valueInst.MatchLdLoc(out var value))
                {
                    return(false);
                }
                if (!(value.Kind == VariableKind.Parameter && value.Index == 0))
                {
                    return(false);
                }
                return(body.Instructions[1].MatchReturn(out var retVal) && retVal.MatchNop());
            }
        }