Информация об изменениях

Сообщение Re[6]: Удобный способ внесения изменений в чужую C#-библиоте от 26.04.2016 2:42

Изменено 26.04.2016 14:35 Albeoris

Заключительная версия, которая заменяет ссылки на старый тип новыми.
Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.

    public sealed class TypeReplaser
    {
        private readonly String _fullName;
        private readonly TypeReference _newReference;
        private readonly TypeDefinition _newDefination;

        private ModuleDefinition _module;
        private MethodDefinition _method;

        public TypeReplaser(String fullName, TypeReference newReference)
        {
            _fullName = fullName;
            _newReference = newReference;
            _newDefination = newReference.Resolve();
        }

        public void Replase(ModuleDefinition module)
        {
            _module = module;
            foreach (TypeDefinition type in _module.Types)
                ProcessType(type);
        }

        private void ProcessType(TypeDefinition type)
        {
            if (type.FullName == _fullName)
                return; // Пропускаем заменяемый тип, иначе не сможем менять приватные члены

            ProcessProperties(type);
            ProcessFields(type);
            ProcessMethods(type);
            ProcessNestedTypes(type);
        }

        private void ProcessNestedTypes(TypeDefinition type)
        {
            if (!type.HasNestedTypes)
                return;

            foreach (TypeDefinition nested in type.NestedTypes)
                ProcessType(nested);
        }

        private void ProcessProperties(TypeDefinition type)
        {
            foreach (PropertyDefinition p in type.Properties)
            {
                if (p.PropertyType.FullName == _fullName)
                    p.PropertyType = _newReference;
            }
        }

        private void ProcessFields(TypeDefinition type)
        {
            foreach (FieldDefinition f in type.Fields)
            {
                if (f.FieldType.FullName == _fullName)
                    f.FieldType = _newReference;
            }
        }

        private void ProcessMethods(TypeDefinition type)
        {
            foreach (MethodDefinition m in type.Methods)
            {
                _method = m;
                ProcessMethod();
            }
        }

        private void ProcessMethod()
        {
            if (_method.ReturnType.FullName == _fullName)
                _method.ReturnType = _newReference;

            ProcessMethodParameters();
            ProcessMethodBody();
        }

        private void ProcessMethodParameters()
        {
            if (!_method.HasParameters)
                return;

            foreach (ParameterDefinition p in _method.Parameters)
            {
                if (p.ParameterType.FullName == _fullName)
                    p.ParameterType = _newReference;
            }
        }

        private void ProcessMethodBody()
        {
            if (!_method.HasBody)
                return;

            ProcessMethodBodyVariables();
            ProcessMethodBodyInstructions();
        }

        private void ProcessMethodBodyVariables()
        {
            if (!_method.Body.HasVariables)
                return;

            foreach (VariableDefinition v in _method.Body.Variables)
            {
                if (v.VariableType.FullName == _fullName)
                    v.VariableType = _newReference;
            }
        }

        private void ProcessMethodBodyInstructions()
        {
            MethodBody body = _method.Body;
            Collection<Instruction> instructions = body.Instructions;
            for (int i = 0; i < instructions.Count; i++)
            {
                Instruction inst = instructions[i];
                switch (inst.OpCode.OperandType)
                {
                    case OperandType.InlineField:
                    case OperandType.InlineMethod:
                    case OperandType.InlineTok:
                    case OperandType.InlineType:
                        break;
                    default:
                        continue;
                }

                IGenericInstance genericInstance = inst.Operand as IGenericInstance;
                if (genericInstance != null && genericInstance.HasGenericArguments)
                {
                    for (int g = 0; g < genericInstance.GenericArguments.Count; g++)
                    {
                        if (genericInstance.GenericArguments[g].FullName == _fullName)
                            genericInstance.GenericArguments[g] = _newReference;
                    }
                }

                TypeReference typeReference = inst.Operand as TypeReference;
                if (typeReference != null)
                {
                    if (typeReference.FullName == _fullName)
                        instructions.Replace(i, RecreateTypeOperand(inst));
                    continue;
                }

                MethodReference methodReference = inst.Operand as MethodReference;
                if (methodReference != null)
                {
                    if (methodReference.DeclaringType.FullName == _fullName)
                        instructions.Replace(i, RecreateMethodOperand(inst));
                    continue;
                }

                FieldReference fieldReference = inst.Operand as FieldReference;
                if (fieldReference != null)
                {
                    if (fieldReference.DeclaringType.FullName == _fullName)
                        instructions.Replace(i, RecreateFieldOperand(inst));
                    continue;
                }

            }
        }

        private Instruction RecreateMethodOperand(Instruction inst)
        {
            String oldName = ((MethodReference)inst.Operand).Name;
            MethodDefinition method = _newDefination.GetMethod(oldName);
            MethodReference methodReference = _module.Import(method);
            return Instruction.Create(inst.OpCode, methodReference);
        }

        private Instruction RecreateFieldOperand(Instruction inst)
        {
            String oldName = ((FieldReference)inst.Operand).Name;
            FieldDefinition field = _newDefination.GetField(oldName);
            FieldReference fieldReference = _module.Import(field);
            return Instruction.Create(inst.OpCode, fieldReference);
        }

        private Instruction RecreateTypeOperand(Instruction inst)
        {
            return Instruction.Create(inst.OpCode, _newReference);
        }
    }
Заключительная версия, которая заменяет ссылки на старый тип новыми.
Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.

    public sealed class TypeReplaser
    {
        private readonly String _fullName;
        private readonly TypeReference _newReference;
        private readonly TypeDefinition _newDefination;

        private ModuleDefinition _module;
        private MethodDefinition _method;

        public TypeReplaser(String fullName, TypeReference newReference)
        {
            _fullName = fullName;
            _newReference = newReference;
            _newDefination = newReference.Resolve();
        }

        public void Replase(ModuleDefinition module)
        {
            _module = module;
            foreach (TypeDefinition type in _module.Types)
                ProcessType(type);
        }

        private void ProcessType(TypeDefinition type)
        {
            if (type.FullName == _fullName)
                return; // Пропускаем заменяемый тип, иначе не сможем менять приватные члены

            ProcessProperties(type);
            ProcessFields(type);
            ProcessMethods(type);
            ProcessNestedTypes(type);
        }

        private void ProcessNestedTypes(TypeDefinition type)
        {
            if (!type.HasNestedTypes)
                return;

            foreach (TypeDefinition nested in type.NestedTypes)
                ProcessType(nested);
        }

        private void ProcessProperties(TypeDefinition type)
        {
            foreach (PropertyDefinition p in type.Properties)
            {
                if (p.PropertyType.FullName == _fullName)
                    p.PropertyType = _newReference;
            }
        }

        private void ProcessFields(TypeDefinition type)
        {
            foreach (FieldDefinition f in type.Fields)
            {
                if (f.FieldType.FullName == _fullName)
                    f.FieldType = _newReference;
            }
        }

        private void ProcessMethods(TypeDefinition type)
        {
            foreach (MethodDefinition m in type.Methods)
            {
                _method = m;
                ProcessMethod();
            }
        }

        private void ProcessMethod()
        {
            if (_method.ReturnType.FullName == _fullName)
                _method.ReturnType = _newReference;

            ProcessMethodParameters();
            ProcessMethodBody();
        }

        private void ProcessMethodParameters()
        {
            if (!_method.HasParameters)
                return;

            foreach (ParameterDefinition p in _method.Parameters)
            {
                if (p.ParameterType.FullName == _fullName)
                    p.ParameterType = _newReference;
            }
        }

        private void ProcessMethodBody()
        {
            if (!_method.HasBody)
                return;

            ProcessMethodBodyVariables();
            ProcessMethodBodyInstructions();
        }

        private void ProcessMethodBodyVariables()
        {
            if (!_method.Body.HasVariables)
                return;

            foreach (VariableDefinition v in _method.Body.Variables)
            {
                if (v.VariableType.FullName == _fullName)
                    v.VariableType = _newReference;
            }
        }

        private void ProcessMethodBodyInstructions()
        {
            MethodBody body = _method.Body;
            Collection<Instruction> instructions = body.Instructions;
            for (int i = 0; i < instructions.Count; i++)
            {
                Instruction inst = instructions[i];
                switch (inst.OpCode.OperandType)
                {
                    case OperandType.InlineField:
                    case OperandType.InlineMethod:
                    case OperandType.InlineTok:
                    case OperandType.InlineType:
                        break;
                    default:
                        continue;
                }

                IGenericInstance genericInstance = inst.Operand as IGenericInstance;
                if (genericInstance != null && genericInstance.HasGenericArguments)
                {
                    for (int g = 0; g < genericInstance.GenericArguments.Count; g++)
                    {
                        if (genericInstance.GenericArguments[g].FullName == _fullName)
                            genericInstance.GenericArguments[g] = _newReference;
                    }
                }

                TypeReference typeReference = inst.Operand as TypeReference;
                if (typeReference != null)
                {
                    if (typeReference.FullName == _fullName)
                        instructions.Replace(i, RecreateTypeOperand(inst));
                    continue;
                }

                MethodReference methodReference = inst.Operand as MethodReference;
                if (methodReference != null)
                {
                    if (methodReference.DeclaringType.FullName == _fullName)
                        instructions.Replace(i, RecreateMethodOperand(inst));
                    continue;
                }

                FieldReference fieldReference = inst.Operand as FieldReference;
                if (fieldReference != null)
                {
                    if (fieldReference.DeclaringType.FullName == _fullName)
                        instructions.Replace(i, RecreateFieldOperand(inst));
                    continue;
                }

            }
        }

        private Instruction RecreateMethodOperand(Instruction inst)
        {
            MethodDefinition method = _newDefination.GetMethod((MethodReference)inst.Operand);
            MethodReference methodReference = _module.Import(method);
            return Instruction.Create(inst.OpCode, methodReference);
        }

        private Instruction RecreateFieldOperand(Instruction inst)
        {
            String oldName = ((FieldReference)inst.Operand).Name;
            FieldDefinition field = _newDefination.GetField(oldName);
            FieldReference fieldReference = _module.Import(field);
            return Instruction.Create(inst.OpCode, fieldReference);
        }

        private Instruction RecreateTypeOperand(Instruction inst)
        {
            return Instruction.Create(inst.OpCode, _newReference);
        }
    }



public static MethodDefinition GetMethod(this TypeDefinition td, MethodReference descriptor)
        {
            foreach (MethodDefinition method in td.Methods)
            {
                if (method.Name != descriptor.Name)
                    continue;

                if (method.ReturnType.FullName != descriptor.ReturnType.FullName)
                    continue;

                if (method.HasGenericParameters)
                {
                    if (!descriptor.HasGenericParameters)
                        continue;

                    if (method.GenericParameters.Count != descriptor.GenericParameters.Count)
                        continue;

                    for (int i = 0; i < method.GenericParameters.Count; i++)
                    {
                        if (method.GenericParameters[i].FullName != descriptor.GenericParameters[i].FullName)
                            continue;
                    }
                }
                else if (descriptor.HasGenericParameters)
                {
                    continue;
                }

                if (method.HasParameters)
                {
                    if (!descriptor.HasParameters)
                        continue;

                    if (method.Parameters.Count != descriptor.Parameters.Count)
                        continue;

                    for (int i = 0; i < method.Parameters.Count; i++)
                    {
                        ParameterDefinition p1 = method.Parameters[i];
                        ParameterDefinition p2 = descriptor.Parameters[i];
                        if (p1.ParameterType.FullName != p2.ParameterType.FullName)
                            continue;

                        if (p1.Attributes != p2.Attributes)
                            continue;
                    }
                }
                else if (descriptor.HasParameters)
                {
                    continue;
                }

                return method;
            }

            throw new InvalidOperationException("Sequence contains more than one matching element.");
        }