基于Mono.Cecil的静态注入

Aop注入有2种方式:动态注入和静态注入,其中动态注入有很多实现了

动态注入有几种方式:

  1. 利用Remoting的ContextBoundObject或MarshalByRefObject。
  2. 动态代理(反射),很多AOP框架都用这种方式。
  3. MVC的filter,也是反射。

这里主要介绍静态注入

==========================================================================================

起初的想法是实现一种功能,自动给对象的属性一个默认值,想来想去没有什么好的解决方法,参考资料后决定使用Mono.Cecil修改生成的程序集来实现!

先定义一个接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace System.Linq
{

    /// <summary>
    /// 支持注入
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal interface IInject<T>
    {
        /// <summary>
        /// 注入属性
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        IInject<T> InjectProperty<TKey>(Expression<Func<T, TKey>> propertyName, Expression<Func<T, TKey>> propertyValue, InjectType type);

    }
}

定义一个描述类:

    internal class InjectItem
    {

        public string Name { get; set; }

        /// <summary>
        /// 表示生成的字段
        /// </summary>
        public FieldDefinition Field { get; set; }
        /// <summary>
        /// 标识生成字段成功
        /// </summary>
        public FieldDefinition SetOKField { get; set; }
        /// <summary>
        /// 标识
        /// </summary>
        public FieldDefinition ValueField { get; set; }

        public Type FieldType { get; set; }

        public Type Type { get; set; }

        public Delegate Value { get; set; }

        public MethodReference Method { get; set; }

    }

定义一个缓存帮助类,存放程序集的一些数据:

using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace System
{
    internal class CacheHelper
    {

        internal static readonly ICache<string, TupleCache<AssemblyDefinition, bool>> _assembly = CacheFactory.CreateCache<string, TupleCache<AssemblyDefinition, bool>>();

        internal static readonly List<InjectItem> _setValue = new List<InjectItem>();

        /// <summary>
        /// 得到唯一程序集
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static TupleCache<AssemblyDefinition, bool> GetAssembly(Type type)
        {
            var ass = type.Assembly;
            var path = ass.CodeBase.TrimStart("file:///".ToCharArray());
            return _assembly.Get(path, () =>
            {
                return new TupleCache<AssemblyDefinition, bool>(AssemblyDefinition.ReadAssembly(path), false);
            });
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace System
{
    internal class TupleCache<T1, T2>
    {
        public TupleCache(T1 item1, T2 item2)
        {
            Item1 = item1;
            Item2 = item2;
        }

        public T1 Item1 { get; set; }

        public T2 Item2 { get; set; }
    }
}

定义一个枚举,标识怎么处理映射:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace System.Linq
{
    /// <summary>
    /// 指定注入类型
    /// </summary>
    //[Flags]
    public enum InjectType
    {
        /// <summary>
        /// 忽略原属性值
        /// </summary>
        IgnoreOldValue = 1,
        /// <summary>
        /// 检查原属性默认值
        /// </summary>
        CheckOldValueDefault = 2,
        /// <summary>
        /// 当属性值改变时重新赋值
        /// </summary>
        ReadOnValueChanged = 3,
        /// <summary>
        /// 当属性值改变时重新赋值(检查属性默认值)
        /// </summary>
        ReadOnValueChangedCheckOldValueDefault = 4
    }
}

接下来是具体实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection.Emit;
using System.Linq.Expressions;
using System.Reflection;
using Mono.Cecil;
using System.Collections;
using System.Collections.ObjectModel;
using Mono.Cecil.Cil;

namespace System.Linq
{
    internal class InjectItem
    {

        public string Name { get; set; }

        /// <summary>
        /// 表示生成的字段
        /// </summary>
        public FieldDefinition Field { get; set; }
        /// <summary>
        /// 标识生成字段成功
        /// </summary>
        public FieldDefinition SetOKField { get; set; }
        /// <summary>
        /// 标识
        /// </summary>
        public FieldDefinition ValueField { get; set; }
        /// <summary>
        /// 字段类型
        /// </summary>
        public Type FieldType { get; set; }
        /// <summary>
        /// 属性类型
        /// </summary>
        public Type Type { get; set; }

        public Delegate Value { get; set; }

        public MethodReference Method { get; set; }

    }

    internal sealed class InjectBase<T> : IInject<T>
    {
        /// <summary>
        /// 生成的字段名
        /// </summary>

        static readonly string _fieldName = "___Inject_Property_Name_{0}___";

        static readonly string _setFieldName = "___Inject_Property_Name_{0}___OK";

        static readonly string _methodName = "___Inject_Method_CheckDefaultValue___";

        Collection<InjectItem> _injectItems = new Collection<InjectItem>();

        internal InjectBase()
        {
            _type = typeof(T);
            if (_type.IsNested)
            {
                throw new ArgumentException(_type.FullName + " is a private class!");
            }
            _assemby = CacheHelper.GetAssembly(_type);
            _typeDefinition = GetTypeFromAssembly();
            if (_typeDefinition == null)
            {
                throw new ArgumentException("type is undefined!");
            }

        }
        /// <summary>
        /// 要处理的类型
        /// </summary>
        Type _type;

        /// <summary>
        /// 当前类型所对应的Mono.Cecil描述程序集
        /// </summary>

        TupleCache<AssemblyDefinition, bool> _assemby;

        /// <summary>
        /// 当前类型对应的Mono.Cecil描述类型
        /// </summary>
        TypeDefinition _typeDefinition;

        /// <summary>
        /// 获取类型对应的Mono.Cecil描述类型
        /// </summary>
        /// <returns></returns>
        TypeDefinition GetTypeFromAssembly()
        {
            foreach (var item in _assemby.Item1.Modules)
            {
                var type = item.Types.FirstOrDefault(o => o.FullName == _type.FullName);
                if (type != null)
                {
                    return type;
                }
            }
            return null;
        }

        Tuple<InjectItem, bool> OnInject<TKey>(Expression<Func<T, TKey>> propertyName, Expression<Func<T, TKey>> propertyValue)
        {
            //if (!typeof(MemberExpression).IsAssignableFrom(propertyName.Body.GetType()) && propertyName.Body.GetType().Name != "PropertyExpression")
            if (propertyName.Body.GetType().Name != "PropertyExpression")
            {
                throw new ArgumentException("propertyName is not a property expression!");
            }

            var body = propertyName.Body as MemberExpression;

            string name = body.Member.Name;

            //指示是否处理过了当前类型
            TypeDefinition newType = _assemby.Item1.MainModule.Types.FirstOrDefault(o => o.FullName == InjectMap._classType.Item1 + "." + InjectMap._classType.Item2);

            //字段
            string fieldName = string.Format(_fieldName, name);

            if (newType != null)
            {
                //处理过类型的其他属性了

                //看看是否处理了当前属性
                var injectItem1 = _injectItems.FirstOrDefault(o => o.Name == fieldName);
                if (injectItem1 == null)
                {
                    //没处理
                    injectItem1 = new InjectItem()
                    {
                        Name = fieldName,
                        Type = _type
                    };
                    _injectItems.Add(injectItem1);
                    CacheHelper._setValue.Add(injectItem1);
                }
                return new Tuple<InjectItem, bool>(injectItem1, true);
            }
            _assemby.Item2 = true;
            //得到属性
            var property = _typeDefinition.Properties.FirstOrDefault(o => o.Name == name);

            var getMethod = property.GetMethod;

            var setMethod = property.SetMethod;

            //如果不可读
            if (getMethod == null)
            {
                throw new ArgumentException("property " + name + " on " + _type.FullName + " is writeonly!");
            }

            var injectItem = _injectItems.FirstOrDefault(o => o.Name == fieldName);

            if (injectItem == null)
            {
                var field = _typeDefinition.Fields.FirstOrDefault(o => o.Name == fieldName);
                FieldDefinition valueField = null;
                FieldDefinition setFieldOK = null;
                if (field == null)
                {
                    //field = new FieldDefinition(fieldName, Mono.Cecil.FieldAttributes.Private | Mono.Cecil.FieldAttributes.Static, new TypeReference("System", typeof(Func<T, TKey>).Name, ModuleDefinition.ReadModule(Assembly.GetAssembly(typeof(Func<T, TKey>)).Location), new ModuleReference("mscorlib.dll")));

                    field = new FieldDefinition(fieldName, Mono.Cecil.FieldAttributes.Private | Mono.Cecil.FieldAttributes.Static, _typeDefinition.Module.Import(typeof(Func<T, TKey>)));

                    valueField = new FieldDefinition(fieldName + "Value", Mono.Cecil.FieldAttributes.Private, property.PropertyType);
                    setFieldOK = new FieldDefinition(string.Format(_setFieldName, name), Mono.Cecil.FieldAttributes.Private, _typeDefinition.Module.Import(typeof(bool)));
                    _typeDefinition.Fields.Add(field);
                    _typeDefinition.Fields.Add(valueField);
                    _typeDefinition.Fields.Add(setFieldOK);
                    injectItem = new InjectItem()
                    {
                        Field = field,
                        ValueField = valueField,
                        Name = fieldName,
                        Type = _type,
                        FieldType = body.Member.DeclaringType,
                        SetOKField = setFieldOK,
                        Method = new MethodReference(fieldName, _typeDefinition.Module.Import(property.PropertyType), _typeDefinition.Module.Import(_typeDefinition))
                    };
                    _injectItems.Add(injectItem);
                    CacheHelper._setValue.Add(injectItem);
                }
                //else
                //{
                //    valueField = _typeDefinition.Fields.FirstOrDefault(o => o.Name == fieldName + "Value");
                //    setFieldOK = _typeDefinition.Fields.FirstOrDefault(o => o.Name == string.Format(_setFieldName, name));
                //}

            }

            ////DefaultMethod

            //var checkDefaultMethod = _typeDefinition.Methods.FirstOrDefault(o => o.Name == _methodName);

            //if (checkDefaultMethod == null)
            //{
            //    checkDefaultMethod = new MethodDefinition(_methodName, Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.Private, _typeDefinition.Module.Import(typeof(bool)));
            //}

            return new Tuple<InjectItem, bool>(injectItem, false);
        }

        public IInject<T> InjectProperty<TKey>(Expression<Func<T, TKey>> propertyName, Expression<Func<T, TKey>> propertyValue, InjectType type)
        {
            //预处理
            var item = OnInject(propertyName, propertyValue);
            //编译值
            item.Item1.Value = propertyValue.Compile();
            if (item.Item2)
            {
                //已处理,不再继续
                return this;
            }
            var body = propertyName.Body as MemberExpression;
            string name = body.Member.Name;
            var fieldName = string.Format(_fieldName, name);
            var fieldValueName = fieldName + "Value";
            //得到属性
            var property = _typeDefinition.Properties.FirstOrDefault(o => o.Name == name);
            var getMethod = property.GetMethod;
            getMethod.Body.Instructions.Clear();
            getMethod.Body.Variables.Add(new VariableDefinition("flag", _typeDefinition.Module.Import(typeof(bool))));
            getMethod.Body.Variables.Add(new VariableDefinition(fieldValueName, _typeDefinition.Module.Import(property.PropertyType)));

            //处理Get属性
            if (type == InjectType.CheckOldValueDefault || type == InjectType.ReadOnValueChangedCheckOldValueDefault)
            {
                InjectPropertyInternalGetCheckDefault(item.Item1, getMethod, propertyName, propertyValue, type);
            }
            else
            {
                InjectPropertyInternalGet(item.Item1, getMethod, propertyName, propertyValue, type);
            }
            var setMethod = property.SetMethod;

            if (setMethod != null)
            {
                //处理Set属性
                setMethod.Body.Instructions.Clear();
                if (type == InjectType.CheckOldValueDefault || type == InjectType.ReadOnValueChangedCheckOldValueDefault)
                {
                    InjectPropertyInternalSetCheckDefault(item.Item1, setMethod, propertyName, propertyValue, type);
                }
                else
                {
                    InjectPropertyInternalSet(item.Item1, setMethod, propertyName, propertyValue, type);
                }
            }

            return this;
        }

        IInject<T> InjectPropertyInternalGet<TKey>(InjectItem item, MethodDefinition method, Expression<Func<T, TKey>> propertyName, Expression<Func<T, TKey>> propertyValue, InjectType type)
        {
            method.Body.InitLocals = true;
            var iLProcessor = method.Body.GetILProcessor();
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Nop);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_0);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldfld, item.SetOKField);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldfld, item.Item1.ValueField);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldnull);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ceq);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stloc_0);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldloc_0);

            List<Instruction> trueInstruction = new List<Instruction>();
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Nop));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldsfld, item.Field));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            var invokeMethod = _typeDefinition.Module.Import(typeof(Func<T, TKey>).GetMethod("Invoke"));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Callvirt, invokeMethod));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Stfld, item.ValueField));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldc_I4_1));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Stfld, item.SetOKField));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Nop));

            List<Instruction> falseInstruction = new List<Instruction>();
            falseInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            falseInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldfld, item.ValueField));
            falseInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Stloc_1));

            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Brfalse_S, falseInstruction[0]);

            foreach (var instruction in trueInstruction)
            {
                iLProcessor.Append(instruction);
            }

            foreach (var instruction in falseInstruction)
            {
                iLProcessor.Append(instruction);
            }

            Instruction endInstruction = Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldloc_1);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Br_S, endInstruction);
            iLProcessor.Append(endInstruction);

            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ret);
            return this;
        }

        IInject<T> InjectPropertyInternalGetCheckDefault<TKey>(InjectItem item, MethodDefinition method, Expression<Func<T, TKey>> propertyName, Expression<Func<T, TKey>> propertyValue, InjectType type)
        {
            var body = propertyName.Body as MemberExpression;

            //method.Body.Variables.Add(new VariableDefinition("flag2", _typeDefinition.Module.Import(typeof(bool))));

            //if (item.ValueField.FieldType.IsValueType)
            //{
            //    method.Body.Variables.Add(new VariableDefinition("defaultValue", _typeDefinition.Module.Import(body.Member.ReflectedType)));
            //}
            method.Body.InitLocals = true;
            var iLProcessor = method.Body.GetILProcessor();
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Nop);

            List<Instruction> flag1 = new List<Instruction>();
            flag1.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            flag1.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldfld, item.SetOKField));
            flag1.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldc_I4_0));
            flag1.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ceq));
            flag1.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Stloc_0));
            flag1.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldloc_0));

            //校验默认值
            if (!item.ValueField.FieldType.IsValueType)
            {
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldloc_2);
            }
            else
            {
                var equalsMethod = _typeDefinition.Module.Import(typeof(object).GetMethod("Equals", BindingFlags.Static | BindingFlags.Public));
                var getHashCodeMethod = _typeDefinition.Module.Import(body.Member.DeclaringType.GetMethod("GetHashCode"));

                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Bne_Un_S, flag1[0]);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stloc_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldloc_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Bne_Un_S, flag1[0]);

                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldfld, item.SetOKField);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Bne_Un_S, flag1[0]);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_2);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldfld, item.SetOKField);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);

                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_2);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Callvirt, getHashCodeMethod);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ceq);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stloc_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldloc_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Callvirt, getHashCodeMethod);

                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldfld, item.ValueField);

                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Callvirt, getHashCodeMethod);

                //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Call, equalsMethod);
            }

            foreach (var instruction in flag1)
            {
                iLProcessor.Append(instruction);
            }

            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldfld, item.SetOKField);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ceq);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stloc_0);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldloc_0);

            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ceq);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stloc_0);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldloc_0);

            List<Instruction> trueInstruction = new List<Instruction>();
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Nop));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldsfld, item.Field));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            var invokeMethod = _typeDefinition.Module.Import(typeof(Func<T, TKey>).GetMethod("Invoke"));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Callvirt, invokeMethod));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Stfld, item.ValueField));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldc_I4_1));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Stfld, item.SetOKField));
            trueInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Nop));

            List<Instruction> falseInstruction = new List<Instruction>();
            falseInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldarg_0));
            falseInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldfld, item.ValueField));
            falseInstruction.Add(Instruction.Create(Mono.Cecil.Cil.OpCodes.Stloc_1));

            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Brfalse_S, falseInstruction[0]);

            foreach (var instruction in trueInstruction)
            {
                iLProcessor.Append(instruction);
            }

            foreach (var instruction in falseInstruction)
            {
                iLProcessor.Append(instruction);
            }

            Instruction endInstruction = Instruction.Create(Mono.Cecil.Cil.OpCodes.Ldloc_1);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Br_S, endInstruction);
            iLProcessor.Append(endInstruction);

            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ret);
            return this;
        }

        IInject<T> InjectPropertyInternalSet<TKey>(InjectItem item, MethodDefinition method, Expression<Func<T, TKey>> propertyName, Expression<Func<T, TKey>> propertyValue, InjectType type)
        {
            var iLProcessor = method.Body.GetILProcessor();
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_0);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_1);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stfld, item.ValueField);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_0);
            if (type == InjectType.IgnoreOldValue)
            {
                iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_1);
            }
            else if (type == InjectType.ReadOnValueChanged)
            {
                iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
            }
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stfld, item.SetOKField);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ret);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stfld, item.ValueField);
            return this;
        }

        IInject<T> InjectPropertyInternalSetCheckDefault<TKey>(InjectItem item, MethodDefinition method, Expression<Func<T, TKey>> propertyName, Expression<Func<T, TKey>> propertyValue, InjectType type)
        {
            var iLProcessor = method.Body.GetILProcessor();
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_0);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_1);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stfld, item.ValueField);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_0);
            if (type == InjectType.CheckOldValueDefault)
            {
                iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_1);
            }
            else if (type == InjectType.ReadOnValueChangedCheckOldValueDefault)
            {
                iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I4_0);
            }
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stfld, item.SetOKField);
            iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Ret);
            //iLProcessor.Emit(Mono.Cecil.Cil.OpCodes.Stfld, item.ValueField);
            return this;
        }

    }
}

以上就是主要代码,接下来给个入口就好了

using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace System.Linq
{

    public abstract class InjectMap
    {
        /// <summary>
        /// 命名空间和类名,标识程序集已处理
        /// </summary>
        internal static readonly Tuple<string, string> _classType = new Tuple<string, string>("System.Inject", "Inject_Assemby_Over");

        static bool _isMap = false;

        internal static readonly ICache<Type, InjectMap> _cache = CacheFactory.CreateCache<Type, InjectMap>();

        /// <summary>
        /// 创建映射
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static InjectMap<T> CreateMap<T>()
        {
            var map = _cache.Get(typeof(T), () => InjectMap<T>.Create()) as InjectMap<T>;
            return map;
        }

        /// <summary>
        /// 结束注入
        /// 注意:方法会自动检测是否需要注入数据
        /// 如果成功注入数据,则会重启应用程序
        /// 注入数据后不会再次注入了
        /// </summary>
        public static void End()
        {
            bool reload = false;
            foreach (var item in CacheHelper._assembly)
            {
                if (item.Value.Item2)
                {
                    reload = true;
                    item.Value.Item1.MainModule.Types.Add(new TypeDefinition(_classType.Item1, _classType.Item2, Mono.Cecil.TypeAttributes.Class | Mono.Cecil.TypeAttributes.Public, item.Value.Item1.MainModule.Import(typeof(object))));
                    item.Value.Item1.Write(item.Key);
                }
            }
            if (reload)
            {
                //会重启程序
                CacheHelper._assembly.Flush();
                CacheHelper._setValue.Clear();
                _isMap = true;

                if (HttpContext.Current != null)
                {
                    //HttpRuntime.UnloadAppDomain();
                    //HttpRuntime.UnloadAppDomain();
                    File.SetLastWriteTime(System.Web.Hosting.HostingEnvironment.MapPath("~/web.config"), DateTime.Now);
                    //HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Url.ToString());
                }
                return;
            }
            else if (!_isMap)
            {
                //不重启程序,修改静态字段值
                foreach (var item in CacheHelper._setValue)
                {
                    item.Type.GetField(item.Name, Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).SetValue(null, item.Value);
                }
            }
            _isMap = true;
            CacheHelper._assembly.Flush();
            CacheHelper._setValue.Clear();
        }

#if DEBUG
        /// <summary>
        /// 结束注入
        /// </summary>
        public static void End(string testPath)
        {
            bool reload = false;
            foreach (var item in CacheHelper._assembly)
            {
                if (item.Value.Item2)
                {
                    reload = true;
                    item.Value.Item1.MainModule.Types.Add(new TypeDefinition(_classType.Item1, _classType.Item2, Mono.Cecil.TypeAttributes.Class | Mono.Cecil.TypeAttributes.Public));
                    item.Value.Item1.Write(item.Key + testPath + ".dll");
                }
            }
            if (reload)
            {
                _isMap = true;
                if (HttpContext.Current != null)
                {
                    //HttpRuntime.UnloadAppDomain();
                    HttpRuntime.UnloadAppDomain();
                    File.SetLastWriteTime(System.Web.Hosting.HostingEnvironment.MapPath("~/web.config"), DateTime.Now);
                    //HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Url.ToString());
                }
                return;
            }
            else if (!_isMap)
            {
                foreach (var item in CacheHelper._setValue)
                {
                    item.Type.GetField(item.Name, Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).SetValue(null, item.Value);
                }
            }
            _isMap = true;
        }
#endif

    }

    /// <summary>
    /// 支持注入
    /// </summary>
    public sealed class InjectMap<T> : InjectMap
    {

        InjectMap() { }
        IInject<T> _inject;

        internal static InjectMap<T> Create()
        {
            InjectMap<T> map = new InjectMap<T>();
            map._inject = new InjectBase<T>();
            return map;
        }

        /// <summary>
        /// 注入属性值到指定类型中
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="propertyName">属性名表达式</param>
        /// <param name="propertyValue">属性值表达式</param>
        /// <returns></returns>
        public InjectMap<T> InjectProperty<TKey>(Expressions.Expression<Func<T, TKey>> propertyName, Expressions.Expression<Func<T, TKey>> propertyValue)
        {
            this._inject.InjectProperty(propertyName, propertyValue, InjectType.IgnoreOldValue);
            return this;
        }

        /// <summary>
        /// 注入属性值到指定类型中
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="propertyName">属性名表达式</param>
        /// <param name="propertyValue">属性值表达式</param>
        /// <param name="type">注入属性选项</param>
        /// <returns></returns>
        public InjectMap<T> InjectProperty<TKey>(Expressions.Expression<Func<T, TKey>> propertyName, Expressions.Expression<Func<T, TKey>> propertyValue, InjectType type)
        {
            this._inject.InjectProperty(propertyName, propertyValue, type);
            return this;
        }
    }

}

测试:

    public class TestClass
    {
        public string Name { get; set; }

        public int Id { get; set; }

        public bool IsDeleted { get; set; }
    }

Application_Start中执行代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace Dai.CommonLib.WebTest.Models
{
    class Register : IRegister
    {
        void IRegister.Register()
        {
            InjectMap.CreateMap<TestClass>().InjectProperty(o => o.Name, o => "test").InjectProperty(o => o.IsDeleted, o => !string.IsNullOrEmpty(o.Name));
            InjectMap.End();
        }
    }
}

控制器代码:

using Dai.CommonLib.WebTest.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Dai.CommonLib.WebTest.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            TestClass testClass = new TestClass();

            return Content(testClass.ToJson());
        }
    }
}

看看效果

第一次访问:

{"Name":null,"Id":0,"IsDeleted":false}

此时程序会重启,并且Mono.Cecil会修改已生成好的程序集(如果没有修改过的话)

第二次及以后的访问:

{"Name":"test","Id":0,"IsDeleted":true}

这时候程序集的IL代码已经被修改了

优点:使用方便,可以省去一些麻烦

缺点:目前来看应用场景并不多,并且无法调试(因为程序集与源代码不匹配)!!!

不管怎么说,效果是有的

时间: 2025-01-11 04:47:43

基于Mono.Cecil的静态注入的相关文章

编译时MSIL注入--实践Mono Cecil(1)

原文:编译时MSIL注入--实践Mono Cecil(1) 紧接上两篇浅谈.NET编译时注入(C#-->IL)和浅谈VS编译自定义编译任务—MSBuild Task(csproject),在第一篇中我们简单研究了c#语法糖和PostSharp的MSIl注入,紧接第二篇中我们介绍了自定义MSBuild编译任务(记得有位老兄发链接用 MSBuild自动发布Silverlight xap ,我想说的我做的是自定义编译任务,不是什么发布,MSBuild本就是一个发布工具).之所以在此前介绍编译Task是

使用Mono Cecil 动态获取运行时数据 (Atribute形式 进行注入) -摘自网络

目录 一:普通写法 二:注入定义 三:Weave函数 四:参数构造 五:业务编写 六:注入调用 一:普通写法 1 2 3 4 public static string GetPoint(int x, int y)  {     var value=x; } 哇 好简单啊.其实动态获取和我们普通这样写代码是一样的,我们把要注入的代码,生成一个接收的变量就可以了. 就像上面value 一样接收,然后传递给我们自己函数就可以了. 二 :注入定义 public class WeaveService :

日志系统实战(一)-AOP静态注入

背景 近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息.这时候就想到用Aop的方式了. 技术分析 AOP分动态注入和静态注入. 动态注入方式 1:Remoting的ContextAttribute上下文(性能差). 2:动态代理(反射),大多AOP框架都用这种方式. 3:MVC的filter,也是反射. 第一种:性能太差不考虑.第二种:为了记日志,生产环境都用动态代理,性能损耗不小,不推荐.第三种:只有UI层能用.其他层和第二种一样. 静态注入方式 (本文重点). 1:基于IL

使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试(续)

本文严禁转载. 之前的方法及其局限 问题背景和最初的尝试见这里.最开始的想法比较简单,只想着利用 PostprocessBuild 这个事件,来对已经准备好的本地工程文件(iOS 或 Android)中的 .NET 程序集进行注入.但是,这样做限制很多. 首先,无法对 IL2CPP 作为 Scripting Backend 的情况进行注入.因为触发这个事件时,本地工程文件中没有 .NET 程序集,只有 C++ 代码,无法用 Cecil 进行注入. 第二,Android 平台,用 Mono2x 作

利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

原文:利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还可以修改已有的程序集,并且它支持多个运行时框架上例如:.net2.0/3.5/4.0,以及silverlight程序 官方地址:http://www.mono-project.com/Cecil 首先,我先假想有一个这样的商业组件,该组件满足了以下条件: 1. 该程序集的代码被混淆过了 2. 该程序

使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试

Unity3D 引擎在  UnityEngine 名字空间下,提供了  Profiler 类(Unity 5.6 开始似乎改变了这个名字空间),用于辅助对项目性能进行测试.以 Android 平台为例,在构建之前,需要在 Unity 的 File/Build Settings 菜单项弹出的窗口中,勾选 Development Build 一项.后用  adb forward  的方式,将 Android 设备的 TCP 输出转发到电脑,实现和 Unity Profiler 的连接(网上很容易找到

springboot-31-springboot静态注入

springboot中 使用 @Autowired 注入时, 是可以为静态变量进行注入的 package com.iwhere.footmark.tools; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.

Mono.Cecil 修改目标.NET的IL代码保存时报异常的处理。

使用Mono.Cecil对目标.NET的DLL程序进行IL修改后保存时报"Failed to resolve assembly: ' xxxxxx, version=xxxxx,Culture=xxxxx,PublicKeyToken=xxxxxxx'"异常. 使用DNSPY进行调试,原来是因为修改的目标DLL和本身程序不在同一个目录,当需要用到目标DLL引用的DLL时Mono.Cecil只在自身所在的目录进行搜索匹配, 找不到对应的DLL文件就会报上面的异常信息. 解决方法有两种:

Mono.Cecil - 0.6

原文:Mono.Cecil - 0.6 项目地址:Mono.Cecil 项目描述:In simple English, with Cecil, you can load existing managed assemblies, browse all the contained types, modify them on the fly and save back to the disk the modified assembly. 类似项目:Microsoft CCI Common Compil