同步操作将从 New佳佳/NearAop 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
自己实现的简单AOP及代码混淆。支持 .NET Framework 4.X , .NET Core 3.0。目前处于测试阶段,请勿在生产环境下使用! 交流群:438789123 ,51492982
Install-Package NearAop
VS菜单: 项目 > 管理 NuGet 程序包 > 输入NearAop
卸载时需要同时卸载NearAop.Handler
,否则编译时出现 AopTask”任务意外失败。
3 、方法代理
4、接口代理
5、SetProxySourceAttribute特性
6、SetCancelAopAttribute特性
7、AOP调试信息输出
8、代码混淆
编译时自动实现INotifyPropertyChanged
接口。当属性值变更时触发事件来通知处理程序。
类型 | 用途 |
---|---|
AddNotifyPropertyChangedAttribute(特性) | 标记要处理的类或者成员属性。 |
IAopHandlerNotifyPropertyChanged(接口) | 用于对外提供触发事件的函数接口。 |
c#
编译前:
[AddNotifyPropertyChanged]
class NotifyPropertyChangedDemo
{
public int MyProperty { get; set; }
}
编译后:
[AddNotifyPropertyChanged]
internal class NotifyPropertyChangedDemo : INotifyPropertyChanged, IAopHandlerNotifyPropertyChanged
{
private int _MyProperty;
public int MyProperty
{
get
{
return _MyProperty;
}
set
{
// 赋值比较可用 AddNotifyPropertyChanged(IsTestEquals = false) 关闭。
if (_MyProperty != value)
{
_MyProperty = value;
IAopHandlerNotifyPropertyChanged.RaiseEvent(this, "MyProperty");
}
}
}
//自动添加事件
public event PropertyChangedEventHandler _nearAop_PropertyChanged;
//自动添加的RaiseEvent方法。
void IAopHandlerNotifyPropertyChanged.RaiseEvent(object sender, string propertyName)
{
PropertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
}
}
1、由于自动生成了
PropertyChanged
事件和IAopHandlerNotifyPropertyChanging.RaiseEvent
方法,因此在编译前无法使用。如需调用可以自行实现IAopHandlerNotifyPropertyChanging
接口,代码实现参考上面的例子,或使用NearAop.AopHelper.RaiseEventPropertyChanged
方法。2、静态成员需要自行添加 PropertyChangedEventHandler静态事件字段。
vb.net
编译前:
Imports NearAop
<AddNotifyPropertyChanged>
Public Class NotifyPropertyChangedDemo
Property MyProperty As Integer
End Class
编译后:
<AddNotifyPropertyChanged>
Public Class NotifyPropertyChangedDemo
Implements INotifyPropertyChanged, IAopHandlerNotifyPropertyChanged
Private _MyProperty As Integer
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property MyProperty As Integer
Get
Return _MyProperty
End Get
Set(value As Integer)
’赋值比较可用 AddNotifyPropertyChanged(IsTestEquals:=False) 关闭
If _MyProperty <> value Then
_MyProperty = value
[RaiseEvent](Me, "MyProperty")
End If
End Set
End Property
Sub [RaiseEvent](sender As Object, propertyName As String) Implements IAopHandlerNotifyPropertyChanged.[RaiseEvent]
RaiseEvent PropertyChanged(sender, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
编译时自动为属性Get\Set方法植入调用代码。与属性变更通知
的差异是可以获取到属性变更前后的值。
特性 | 对应接口 | 用途 |
---|---|---|
AddProxyPropertyAttribute | IAopHandlerProperty | 用于标记要处理的类或属性,等效于同时处理Set\Get方法 |
AddProxyPropertySetAttribute | IAopHandlerPropertySet | 用于标记要处理的类或属性,处理Set方法 |
AddProxyPropertyGetAttribute | IAopHandlerPropertyGet | 用于标记要处理的类或属性,处理Get方法 |
本地代理可以直接实现IAopHandlerPropertyLocal
接口,等效下面的代码。
编译前:
[AddProxyProperty]
class ProxyPropertyDemo : IAopHandlerProperty
{
public int MyProperty { get; set; }
public void PropertySetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, T value, Func<T> getValue, Action<T> setValue)
{
var propName = aopParamInfo.GetMemberName();
Console.WriteLine($"{sender}.{propName}被修改, 修改前:{getValue()}, 新值:{value}");
setValue(value);//执行修改
}
public T PropertyGetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, Func<T> getValue)
{
var propName = aopParamInfo.GetMemberName();
var result = getValue();
var msg = $"{sender}.{propName}被读取,值:{result}";
Console.WriteLine(msg);
return result;
}
}
编译后:
[AddProxyProperty]
internal class ProxyPropertyDemo : IAopHandlerProperty, IAopHandlerPropertySet, IAopHandlerPropertyGet
{
private int _MyPropert;
private static Lazy<AopParamInfo> _nearAop_Info = AopParamInfo.CreateFactory<ProxyPropertyDemo>(385875971);
public int MyProperty
{
get
{
return ((IAopHandlerPropertyGet)this).PropertyGetHandler(_nearAop_Info, this, new _nearAop_get_MyProperty);
}
set
{
//赋值比较可用 AddProxyProperty(IsTestEquals = false) 关闭
if (_MyPropert != value)
{
((IAopHandlerPropertySet)this).PropertySetHandler(_nearAop_Info, this, value, _nearAop_get_MyProperty, _nearAop_set_MyProperty));
}
}
}
public void PropertySetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, T value, Func<T> getValue, Action<T> setValue)
{
//---代码同上
}
public T PropertyGetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, Func<T> getValue)
{
//---代码同上
}
private int _nearAop_get_MyProperty() => _MyPropert;
private void _nearAop_set_MyProperty(int value) => _MyPropert = value;
}
多个类使用同一个实现了IAopHandlerProperty
接口的代理类处理。如果代理类(MyProxy)构造函数中有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性).
[AddProxyProperty(typeof(MyProxy))]
class ProxyPropertyDemo1
{
public int MyProperty { get; set; }
}
[AddProxyProperty(typeof(MyProxy))]
class ProxyPropertyDemo2
{
public int MyProperty { get; set; }
}
class MyProxy : IAopHandlerProperty
{
public void PropertySetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, T value, Func<T> getValue, Action<T> setValue)
{
//---代码同上
}
public T PropertyGetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, Func<T> getValue)
{
//---代码同上
}
}
编译时在原函数中植入代码,在进入函数和退出函数时调用接口实现代码植入。(进入函数时可以修改和读取函数中的参数值)
特性 | 对应接口 | 用途 |
---|---|---|
AddProxyMethodAttribute | IAopHandlerMethod | 用于标记类或成员函数,包含(OnEntry和OnExit) |
AddProxyMethodEntryAttribute | IAopHandlerMethod | 标记进入函数时执行。 |
AddProxyMethodExitAttribute | IAopHandlerMethod | 标记退出函数时执行。 |
编译前:
[AddProxyMethod(typeof(Proxy2), IsUpdateValue = true, IsSetValueOnExit = true)]
class ProxyMethodDemo
{
private int value1;
private int value2;
//要植入代码的方法
public void SetValue(int value1, int value2)
{
this.value1 = value1;
this.value2 = value2;
}
//要植入代码的方法
public string GetValue(int value1, int value2)
{
return $"{this.value1} . {this.value2} . {value1} . {value2}";
}
}
//代理类,用于处理进入函数和退出函数时的逻辑
class Proxy2 : IAopHandlerMethod
{
//进入函数时执行
public void OnEntry(MethodArgs methodArgs)
{
//---------------------------获取参数的值
//通过索引获取指定位置的参数值
var value1 = methodArgs.TryGetValue<int>(0);
//通过参数名称获取参数值
var value2 = methodArgs.TryGetValue<int>("value2");
//---------------------------修改参数值
//通过索引修改指定位置的的参数值
methodArgs.TrySetValue(0, 192);
//通过参数名称修改参数值
methodArgs.TrySetValue("value2", 168);
//获取全部的参数值
var values = string.Join(",", methodArgs.ParamValues);
//获取方法名
var methodName = methodArgs.MethodInfo.Name;
Console.WriteLine($"进入[{methodName}]方法时, value1 = {value1} , value2 = {value2}, 全部参数值 = {values}");
}
//退出函数时执行
public TResult OnExit<TResult>(MethodArgs methodArgs, TResult result)
{
var methodName = methodArgs.MethodInfo.Name;
return (TResult)(object)$"{methodName}方法返回 : {result}";
}
}
编译后:
[AddProxyMethod(typeof(Proxy2), IsUpdateValue = true, IsSetValueOnExit = true)]
internal class ProxyMethodDemo
{
private int value1;
private int value2;
private static Lazy<AopParamInfo> _nearAop_Info1 = AopParamInfo.CreateFactory<ProxyMethodDemo>(100663328);
//自动添加的代理类型字段。如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)
private Proxy2 _nearAop_Porxy = new Proxy2();
public void SetValue(int value1, int value2)
{
var methodArgs = new MethodArgs<int, int>(_nearAop_Info1, this);
//第一次获取参数
methodArgs.SetValue(value1, value2);
var aopHandlerMethod = AopHelper.TestNull<IAopHandlerMethod>("_nearAop_Porxy", _nearAop_Porxy);
//调用OnEntry方法
aopHandlerMethod.OnEntry(methodArgs);
//!!!!!!执行参数值修改(默认关闭,特性 IsUpdateValue = true 时开启)
methodArgs.OutValue(out value1, out value2);
//执行原方法代码
this.value1 = value1;
this.value2 = value2;
//!!!!!!第二次获取参数(默认关闭,特性 IsSetValueOnExit = true 时开启)
methodArgs.SetValue(value1, value2);
//调用OnExit方法
aopHandlerMethod.OnExit<object>(methodArgs, null);
}
public string GetValue(int value1, int value2)
{
//同上
}
}
执行结果:
var test = new ProxyMethodDemo();
test.SetValue(1, 2);
var result = GetValue(3, 4);
// result == "GetValue方法返回 : 192 . 168 . 192 . 168"
使用特性的HandlerName
属性指定处理函数名称。
[AddProxyMethod(typeof(Proxy3), HandlerName = "自定义函数")]
class ProxyMethodDemo
{
/// <summary>
/// 实例函数
/// </summary>
public void SetValue1(int value) { }
/// <summary>
/// 静态函数
/// </summary>
public static void SetValue2(int value) { }
}
class Proxy3
{
//注意:函数签名与IAopHandlerMethod.OnEntry 一致
public void 自定义函数(MethodArgs methodArgs)
{
var info = methodArgs.MethodInfo;
var staticStr = info.IsStatic ? "静态" : "";
Console.WriteLine($"进入{staticStr}函数[{info.DeclaringType}.{info.Name}]");
}
//注意:函数签名与IAopHandlerMethod.OnExit 一致
public TResult 自定义函数<TResult>(MethodArgs methodArgs, TResult result)
{
var info = methodArgs.MethodInfo;
var staticStr = info.IsStatic ? "静态" : "";
Console.WriteLine($"退出{staticStr}函数[{info.DeclaringType}.{info.Name}]");
return result;
}
}
由于编译前代理字段
可能为自动生成,用于配合代理模式
使用。执行时会在类中实现目标接口,并将接口实现指向代理字段。如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)。如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)
特性 | 参数1 | 参数2 |
---|---|---|
AddProxyInterface | Type:代理类型 | Type[]:目标接口 |
编译前:
[AddProxyInterface(typeof(Proxy4),typeof(IMyInterface1),typeof(IMyInterface2))]
class ProxyInterfaceDemo
{
}
interface IMyInterface1
{
void ShowMsg();
}
interface IMyInterface2
{
}
//代理类要求实现目标接口
class Proxy4: IMyInterface
{
public void ShowMsg()
{
Console.WriteLine("接口代理测试成功!");
}
}
编译后:
[AddProxyInterface(typeof(Proxy4), typeof(IMyInterface))]
internal class ProxyInterfaceDemo : IMyInterface, IMyInterface2
{
//如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)
private Proxy4 _nearAop_Porxy = new Proxy4();
void IMyInterface.ShowMsg()
{
AopHelper.TestNull<IMyInterface>("_nearAop_Porxy", _nearAop_Porxy).ShowMsg();
}
}
用于标记代理类字段
,通常情况下代理类字段
由AOP自动生成,但在代理类
构造函数中有参数的情况下不会自动初始化实例,需用户显式初始化。
!如自动初始化失败,并且用户没有手动初始化,运行时会抛出异常:
_nearAop_Porxy..... is empty,未将对象引用设置到对象的实例.
[AddProxyInterface(typeof(Proxy5), typeof(IMyInterface1))]
[AddProxyInterface(typeof(Proxy6), typeof(IMyInterface2))]
class ProxySourceDemo
{
//(用户显式初始化)直接使用字段类型
[SetProxySource]
readonly Proxy5 proxy5 = new Proxy5("接口1");
//(用户显式初始化)使用type
[SetProxySource(typeof(Proxy6))]
readonly object proxy6 = new Proxy6("接口2");
}
class Proxy5 : IMyInterface1
{
private readonly string p1;
public Proxy5(string p1) => this.p1 = p1;
}
class Proxy6 : IMyInterface2
{
private readonly string p1;
public Proxy6(string p1) => this.p1 = p1;
}
标记SetCancelAopAttribute特性的成员或类型,AOP将忽略代码植入操作。
返回目录
在项目的输出文件目录obj\Debug
或obj\Release
目录下新建文件NearAop.Debug
返回目录
在编译时对输出的(Dll、Exe)文件,进行混淆,不会影响源码。
项目中新建任意名称的Class
并继承NearAop.ObfuscatedOption
。
C#:
class ObfuscatedOption : NearAop.ObfuscatedOption
{
//没有自定义需求的情况下可忽略构造函数!
public ObfuscatedOption()
{
//短名称文本,启用后成员将被修改为 "O火星文O";
TargetName = "火星文";
//混淆符号,启用后会替代O,要求大于16个字符,并且不能相同;
Key = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
//注:某些字符无法用在成员变量上;
}
}
编译后:
VB.NET
Public Class ObfuscatedOption
Inherits NearAop.ObfuscatedOption
'没有自定义需求的情况下可忽略构造函数!
Sub New()
'短名称文本,启用后成员将被修改为 "O火星文O"
TargetName = "火星文"
'混淆符号,启用后会替代O,要求大于16个字符,并且不能相同;
Key = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"
'注:某些字符无法用在成员变量上;
End Sub
End Class
编译后:
返回目录
某些情况下需要反射成员,使用SetCancelObfuscatedAttribute
特性标记指定的成员,可以取消混淆行为。
//如果没有使用构造函数,将自动判断标记的成员类型
//等同于 [SetCancelObfuscated(MemberType.Type)]
[SetCancelObfuscated]
class Test
{
//等同于 [SetCancelObfuscated(MemberType.Method)]
[SetCancelObfuscated]
public void SetValue() { }
//同时取消“函数”和“参数”的命名混淆
[SetCancelObfuscated(MemberType.Method | MemberType.Parameter)]
public void SetValue2(string param) { }
}
如果类型被定义为Public
其中被定义为Public
的成员不会被混淆。否则外部引用当前DLL时将无法使用。合理使用访问级别关键字控制混淆覆盖率!
类型被定义为
Public
且成员被定义为Public
时,成员及类型不会被混淆。类型被定义为非
Public
时成员会被混淆。例外:定义一个 internal级别的接口,但实现类是public 的情况也会取消成员的混淆。
c# | Vb | 是否混淆 |
---|---|---|
public | Public | no |
internal | Friend | yes |
protected | Protected | yes |
protected internal | Protected Friend | yes |
private | Private | yes |
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。