diff --git a/.npmignore b/.npmignore index 81b2c9c..a3cc427 100644 --- a/.npmignore +++ b/.npmignore @@ -1,2 +1,4 @@ VSProj~ -.github \ No newline at end of file +.github +README.md +Documentation \ No newline at end of file diff --git a/Documentation/usage-decorator.md b/Documentation/usage-decorator.md new file mode 100644 index 0000000..22ad801 --- /dev/null +++ b/Documentation/usage-decorator.md @@ -0,0 +1,125 @@ +# 装饰器 + +值得一提的是,装饰器的实现有以下优势: + +* 调用是0GC,低开销的 +* 实现只需要指定一个`DecoratorAttribute` +* 支持装饰异步函数 + +## 例子 + +下面的例子实现目标方法执行前和后打印信息 + +定义一个装饰器,需要继承自`DecoratorAttribute`,并实现`Decorate`方法 + +```csharp +public class DebugInvocationAttribute:DecoratorAttribute +{ + string info; + + public DebugInvocationAttribute(string _info) + { + info = _info; + } + + void OnCompleted() + { + Debug.Log("end "+info); + } + + protected override R Decorate(InvocationInfo invocation) + { + Debug.Log("begin "+info+string.Join(",",invocation.Arguments)); + // invoke original method + var r = invocation.FastInvoke(); + if(IsAsyncMethod) + { + // delay on async method + invocation.GetAwaiter(r).OnCompleted(OnCompleted); + } + else + { + OnCompleted(); + } + return r; + } +} + +``` + +使用 + +```csharp + +public class Demo:MonoBehaviour{ + + void Start(){ + FixHelper.InstallAll(); + } + + async void Update(){ + if(Input.GetKeyDown(KeyCode.A)){ + Work(1,"foo"); + } + if(Input.GetKeyDown(KeyCode.S)){ + await AsyncWork(2,"bar"); + print("return"); + } + } + + //decorate a standard method + [DebugInvocation("w1")] + int Work(int i, string s){ + Debug.Log("do work"); + return 123+i; + } + + //decorate an async method + [DebugInvocation("aw2")] + async Task AsyncWork(int i, string s){ + Debug.Log("do a lot work"); + await Task.Delay(1000); + return 123+i; + } +} +``` + +## 异步方法补充说明 + +async方法有一个值得斟酌的问题。如果上例的Task换成UniTask。则不会打印return,这是因为Decrote方法覆盖了原本的continuationAction(这与UniTask的实现有关)。使用如下Decorate方法可以解决所有此类问题: + +```csharp + protected override R Decorate(InvocationInfo invocation) + { + Debug.Log("begin "+info+string.Join(",",invocation.Arguments)); + var r = invocation.FastInvoke(); + if(IsAsyncMethod) + { + // delay when its an async method + var awaiter = invocation.GetAwaiter(r); + UniTask.Create(async()=> + { + try + { + while(!invocation.IsAwaiterCompleted(awaiter)) + await UniTask.Yield(); + } + catch {} + finally + { + OnCompleted(); + } + }); + // invocation.GetAwaiter(r).OnCompleted(OnCompleted); + } + else + { + OnCompleted(); + } + return r; + } +``` + +上例使用`UniTask.Create`创建了一个Timer,可以使用其他类似的方法,如自定义MonoBehaviour等。一旦`IsAwaiterCompleted`检查结束,立即执行自定义的`OnCompleted`方法。 + +因为Unity没有官方支持的Timer功能,基于“此库只做自己该做的”原则,这里只是给出提示。 diff --git a/Documentation/usage-di.md b/Documentation/usage-di.md new file mode 100644 index 0000000..fcecba9 --- /dev/null +++ b/Documentation/usage-di.md @@ -0,0 +1,142 @@ +# 依赖注入 + +依赖注入对于模块间解耦帮助很大。 + +## 基本用法 + +在类成员上使用`[SimpleDI]`标签,使其可被注入。 + +```csharp +public interface IFoo +{ + +} + +public class FooA : IFoo +{ + +} + +public class FooB : IFoo +{ + +} + +public class ServiceA +{ + [SimpleDI] public IFoo foo {get;} +} + +``` + +定义规则: + +```csharp +void Start() +{ + // 使用FooA实现IFoo + ServiceContainer.Bind().AsSingle(noLazy:true).To(); +} + +``` + +## 支持注入的成员 + +支持以下成员: + ++ 字段、属性 ++ 静态成员、实例成员 ++ 只读成员、可读可写成员 + +如: + +```csharp +class Foo +{ + [SimpleDI] static IFoo foo; + [SimpleDI] static IFoo foo { get; } + [SimpleDI] static IFoo foo { get; set; } + [SimpleDI] IFoo foo; + [SimpleDI] IFoo foo { get; } + [SimpleDI] IFoo foo { get; set; } +} + +``` + +## 更多控制 + +### 限定DeclaringType + +```csharp +void Init() +{ + // 使用FooA实现IFoo,只对ServiceA中的成员生效 + ServiceContainer.In().Bind().AsSingle(noLazy:true).To(); +} + +``` + +### 构造函数 + +```csharp +interface IFoo { } +class Foo : IFoo +{ + public Foo(string name, int age) + { + Console.WriteLine($"我的伙伴{name},{age}岁了"); + } +} + +void Init() +{ + // output: 我的伙伴小黑儿,3岁了 + ServiceContainer.Bind().AsSingle(noLazy:true).To("小黑儿",3); +} + +``` + +### 生命模式 ScopeMode + ++ Single 单例(默认) ++ Transient 每次获取都是新实例 + +```csharp + // 使用FooA实现IFoo + ServiceContainer.Bind().AsSingle().To(); + ServiceContainer.Bind().AsTransient().To(); +``` + +### 泛型递归 + +对于`IManager`会分别查找IManager,IFoo,IBar,并支持泛型约束。 + +```csharp +interface IFoo +{ + +} + +interface IManager +{ + +} + +class Foo : IFoo +{ + +} + +class Manager : IManager +{ + +} + +``` + +```csharp +void Init() +{ + ServiceContainer.Get>(); // returns Manager +} +``` diff --git a/Documentation/usage-proxy.md b/Documentation/usage-proxy.md new file mode 100644 index 0000000..5e4b541 --- /dev/null +++ b/Documentation/usage-proxy.md @@ -0,0 +1,36 @@ +# 数据代理 + +下面的代码演示实现一个数据代理: + +```csharp +public class Pat : ProxyData // 需要继承ProxyData +{ + public string name { get; set; } + public int age { get; set; } +} + +var pet = new Pet(); +pet.OnSetProperty(key=>{ + print($"set {key}"); +}); +pet.OnGetProperty(key=>{ + print($"get {key}"); +}); + +pet.age += 1; + +/* +output: + get age + set age +*/ + +``` + +有时你需要判断代理类是否被正确注入: + +```csharp +print(pet.IsFixed()); // false +FixHelper.InstallAll(); +print(pet.IsFixed()); // true +``` diff --git a/README.md b/README.md index 0043348..1bbd18b 100644 --- a/README.md +++ b/README.md @@ -104,166 +104,13 @@ FixHelper.InstallAll();// 查找所有注入标记,并使生效 Debug.Log("hello"); //output: [msg] hello ``` -### 装饰器 +## 更多用法 -值得一提的是,装饰器的实现有以下优势: +[装饰器](./Documentation/usage-decorator.md) -* 调用是0GC,低开销的 -* 实现只需要指定一个`DecoratorAttribute` -* 支持装饰异步函数 +[数据代理](./Documentation/usage-proxy.md) -下面的例子实现目标方法执行前和后打印信息 - -定义一个装饰器,需要继承自`DecoratorAttribute`,并实现`Decorate`方法 - -```csharp -public class DebugInvocationAttribute:DecoratorAttribute -{ - string info; - - public DebugInvocationAttribute(string _info) - { - info = _info; - } - - void OnCompleted() - { - Debug.Log("end "+info); - } - - protected override R Decorate(InvocationInfo invocation) - { - Debug.Log("begin "+info+string.Join(",",invocation.Arguments)); - // invoke original method - var r = invocation.FastInvoke(); - if(IsAsyncMethod) - { - // delay on async method - invocation.GetAwaiter(r).OnCompleted(OnCompleted); - } - else - { - OnCompleted(); - } - return r; - } -} - -``` - -使用 - -```csharp - -public class Demo:MonoBehaviour{ - - void Start(){ - FixHelper.InstallAll(); - } - - async void Update(){ - if(Input.GetKeyDown(KeyCode.A)){ - Work(1,"foo"); - } - if(Input.GetKeyDown(KeyCode.S)){ - await AsyncWork(2,"bar"); - print("return"); - } - } - - //decorate a standard method - [DebugInvocation("w1")] - int Work(int i, string s){ - Debug.Log("do work"); - return 123+i; - } - - //decorate an async method - [DebugInvocation("aw2")] - async Task AsyncWork(int i, string s){ - Debug.Log("do a lot work"); - await Task.Delay(1000); - return 123+i; - } -} -``` - -### 异步方法补充说明 - -async方法有一个值得斟酌的问题。如果上例的Task换成UniTask。则不会打印return,这是因为Decrote方法覆盖了原本的continuationAction(这与UniTask的实现有关)。使用如下Decorate方法可以解决所有此类问题: - -```csharp - protected override R Decorate(InvocationInfo invocation) - { - Debug.Log("begin "+info+string.Join(",",invocation.Arguments)); - var r = invocation.FastInvoke(); - if(IsAsyncMethod) - { - // delay when its an async method - var awaiter = invocation.GetAwaiter(r); - UniTask.Create(async()=> - { - try - { - while(!invocation.IsAwaiterCompleted(awaiter)) - await UniTask.Yield(); - } - catch {} - finally - { - OnCompleted(); - } - }); - // invocation.GetAwaiter(r).OnCompleted(OnCompleted); - } - else - { - OnCompleted(); - } - return r; - } -``` - -上例使用`UniTask.Create`创建了一个Timer,可以使用其他类似的方法,如自定义MonoBehaviour等。一旦`IsAwaiterCompleted`检查结束,立即执行自定义的`OnCompleted`方法。 - -因为Unity没有官方支持的Timer功能,基于“此库只做自己该做的”原则,这里只是给出提示。 - -### 数据代理 - -下面的代码演示实现一个数据代理: - -```csharp -public class Pat : ProxyData // 需要继承ProxyData -{ - public string name { get; set; } - public int age { get; set; } -} - -var pet = new Pet(); -pet.OnSetProperty(key=>{ - print($"set {key}"); -}); -pet.OnGetProperty(key=>{ - print($"get {key}"); -}); - -pet.age += 1; - -/* -output: - get age - set age -*/ - -``` - -有时你需要判断代理类是否被正确注入: - -```csharp -print(pet.IsFixed()); // false -FixHelper.InstallAll(); -print(pet.IsFixed()); // true -``` +[依赖注入](./Documentation/usage-di.md) 更多使用方法参考附带的Sample工程 diff --git a/package.json b/package.json index 468a0f1..4d021ac 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,11 @@ "name": "com.bbbirder.injection", "displayName": "Unity Injection", "description": "Unity注入模块,可以运行时改变被注入函数实现。", - "version": "1.3.14", + "version": "1.3.16", "hideInEditor": false, "author": "bbbirder <502100554@qq.com>", "dependencies": { - "com.bbbirder.directattribute": "1.1.4", + "com.bbbirder.directattribute": "1.1.5", "com.unity.nuget.mono-cecil": "1.10.2" }, "samples": [