diff --git a/README.md b/README.md index 19b9a92..460c268 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ **使用Nuget安装** ```PM -Install-Package CatLib.Core -Version 1.1.4 +Install-Package CatLib.Core -Version 1.2.0 ``` **直接下载发布版本** diff --git a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj index 9c78109..8d4a8d8 100644 --- a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj +++ b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj @@ -65,26 +65,32 @@ + + + + + - - - - - - + + + + + + + + + + - - - @@ -99,6 +105,7 @@ + @@ -113,16 +120,13 @@ - - + - - diff --git a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj index de80f89..b01e6aa 100644 --- a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj +++ b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj @@ -36,6 +36,8 @@ + + @@ -44,13 +46,13 @@ - + diff --git a/src/CatLib.Core.Tests/CatLib/ApplicationTests.cs b/src/CatLib.Core.Tests/CatLib/ApplicationTests.cs index 8efa32a..7be5d7d 100644 --- a/src/CatLib.Core.Tests/CatLib/ApplicationTests.cs +++ b/src/CatLib.Core.Tests/CatLib/ApplicationTests.cs @@ -120,9 +120,9 @@ public void GetVersionTest() public void MakeAssemblyClass() { var app = new Application(); - var lru = app.MakeWith>(10); + var sortSet = app.Make>(); - Assert.AreNotEqual(null, lru); + Assert.AreNotEqual(null, sortSet); } [TestMethod] @@ -131,7 +131,7 @@ public void TestOn() var app = new Application(); ExceptionAssert.DoesNotThrow(() => { - app.On("hello", (o) => { }); + app.On("hello", () => { }); }); } @@ -145,6 +145,13 @@ public void GetCurrentProcess() Assert.AreEqual(Application.StartProcess.Inited, app.Process); } + [TestMethod] + public void TestDebugLevel() + { + App.DebugLevel = DebugLevels.Dev; + Assert.AreEqual(DebugLevels.Dev, App.DebugLevel); + } + /// /// 重复的引导测试 /// @@ -302,7 +309,7 @@ public void TestOnDispatcher() { var app = MakeApplication(); - app.Listen("testevent", (payload) => + app.Listen("testevent", (object payload) => { Assert.AreEqual("abc", payload); return 123; diff --git a/src/CatLib.Core.Tests/CatLib/FacaedTests.cs b/src/CatLib.Core.Tests/CatLib/FacaedTests.cs index dc65e21..6302e92 100644 --- a/src/CatLib.Core.Tests/CatLib/FacaedTests.cs +++ b/src/CatLib.Core.Tests/CatLib/FacaedTests.cs @@ -17,15 +17,90 @@ namespace CatLib.Tests [TestClass] public class FacaedTests { + public class FacaedTestClass : IFacaedTestClass + { + } + + public interface IFacaedTestClass + { + } + public class TestClassFacade : Facade + { + + } + + public class TestClassFacadeError : Facade + { + + } - public class FacaedTestClass + [TestMethod] + public void FacadeErrorTest() { + var app = new Application(); + app.Bootstrap(); + app.Singleton(); + + var isError = false; + try + { + var data = TestClassFacadeError.Instance; + } + catch (TypeInitializationException) + { + isError = true; + } + Assert.AreEqual(true, isError); } - public class TestClassFacaed : Facade + [TestMethod] + public void FacadeWatchTest() { + var app = new Application(); + app.Bootstrap(); + app.Singleton().Alias(); + var old = TestClassFacade.Instance; + app.Unbind(); + app.Singleton().Alias(); + Assert.AreNotSame(old, TestClassFacade.Instance); + } + + [TestMethod] + public void FacadeWatchTestWithInstance() + { + var app = new Application(); + app.Bootstrap(); + app.Singleton().Alias(); + + var cls = new FacaedTestClass(); + app.Instance(cls); + + Assert.AreSame(cls, TestClassFacade.Instance); + } + + [TestMethod] + public void FacadeMakeFaild() + { + var app = new Application(); + app.Bootstrap(); + app.Singleton().Alias(); + var old = TestClassFacade.Instance; + + Assert.AreNotEqual(null, old); + app.Unbind(); + + var isError = false; + try + { + var data = TestClassFacade.Instance; + } + catch (UnresolvableException) + { + isError = true; + } + Assert.AreEqual(true, isError); } /// @@ -36,15 +111,99 @@ public void FacadeTest() { var app = new Application(); app.Bootstrap(); - var obj = new FacaedTestClass(); + IFacaedTestClass obj = new FacaedTestClass(); app.Singleton((c, p) => { return obj; - }); + }).Alias(); - Assert.AreEqual(obj, TestClassFacaed.Instance); + Assert.AreSame(obj, TestClassFacade.Instance); //double run - Assert.AreEqual(obj, TestClassFacaed.Instance); + Assert.AreSame(obj, TestClassFacade.Instance); + Assert.AreSame(obj, TestClassFacade.Instance); + } + + [TestMethod] + public void FacadeReleaseTest() + { + var app = new Application(); + app.Bootstrap(); + app.Singleton().Alias(); + + var data = TestClassFacade.Instance; + Assert.AreSame(data, TestClassFacade.Instance); + app.Release(); + Assert.AreNotSame(data, TestClassFacade.Instance); + } + + [TestMethod] + public void TestNotStaticBindFacade() + { + var app = new Application(); + app.Bootstrap(); + app.Bind().Alias(); + + var data = TestClassFacade.Instance; + Assert.AreNotSame(data, TestClassFacade.Instance); + Assert.AreNotSame(TestClassFacade.Instance, TestClassFacade.Instance); + } + + [TestMethod] + public void TestBindingStateSwitchSingletonToBind() + { + var app = new Application(); + app.Bootstrap(); + app.Singleton().Alias(); + + var data = TestClassFacade.Instance; + Assert.AreSame(data, TestClassFacade.Instance); + + app.Unbind(); + app.Bind().Alias(); + Assert.AreNotSame(data, TestClassFacade.Instance); + Assert.AreNotSame(TestClassFacade.Instance, TestClassFacade.Instance); + } + + [TestMethod] + public void TestBindingStateSwitchBindToSingleton() + { + var app = new Application(); + app.Bootstrap(); + app.Bind().Alias(); + + var data = TestClassFacade.Instance; + Assert.AreNotSame(data, TestClassFacade.Instance); + Assert.AreNotSame(TestClassFacade.Instance, TestClassFacade.Instance); + + app.Unbind(); + app.Singleton().Alias(); + data = TestClassFacade.Instance; + Assert.AreSame(data, TestClassFacade.Instance); + Assert.AreSame(TestClassFacade.Instance, TestClassFacade.Instance); + } + + [TestMethod] + public void TestNotBind() + { + var app = new Application(); + app.Bootstrap(); + app.Instance(new FacaedTestClass()); + + var data = TestClassFacade.Instance; + Assert.AreSame(data, TestClassFacade.Instance); + + app.Release(); + + var isError = false; + try + { + data = TestClassFacade.Instance; + } + catch (UnresolvableException) + { + isError = true; + } + Assert.AreEqual(true, isError); } } } diff --git a/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs b/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs index 294fb01..3183efe 100644 --- a/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs +++ b/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs @@ -25,5 +25,5 @@ [assembly: Guid("3c9f4024-910c-4881-a04d-34a6c3a09019")] -[assembly: AssemblyVersion("1.1.4.0")] -[assembly: AssemblyFileVersion("1.1.4.0")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.0.0")] diff --git a/src/CatLib.Core.Tests/Support/Container/BindDataTests.cs b/src/CatLib.Core.Tests/Support/Container/BindDataTests.cs index 579b31d..627abc4 100644 --- a/src/CatLib.Core.Tests/Support/Container/BindDataTests.cs +++ b/src/CatLib.Core.Tests/Support/Container/BindDataTests.cs @@ -174,7 +174,7 @@ public void CanAddOnResolving() bindData.OnResolving((bind, obj) => null); - var data = bindData.ExecResolvingDecorator(new Container()); + var data = bindData.TriggerResolving(new Container()); Assert.AreEqual(null, data); } @@ -194,7 +194,7 @@ public void CheckIllegalResolving() } #endregion - #region UnBind + #region Unbind /// /// 能够正常解除绑定 /// @@ -205,8 +205,12 @@ public void CanUnBind() var bindData = container.Bind("CanUnBind", (app, param) => "hello world", false); Assert.AreEqual("hello world", container.Make("CanUnBind").ToString()); - bindData.UnBind(); - Assert.AreEqual(null, container.Make("CanUnBind")); + bindData.Unbind(); + + ExceptionAssert.Throws(() => + { + container.Make("CanUnBind"); + }); } /// @@ -217,7 +221,7 @@ public void CheckIllegalUnBindInput() { var container = new Container(); var bindData = container.Bind("CanUnBind", (app, param) => "hello world", false); - bindData.UnBind(); + bindData.Unbind(); ExceptionAssert.Throws(() => { diff --git a/src/CatLib.Core.Tests/Support/Container/ContainerHelperTests.cs b/src/CatLib.Core.Tests/Support/Container/ContainerHelperTests.cs index 8439fce..a2901c1 100644 --- a/src/CatLib.Core.Tests/Support/Container/ContainerHelperTests.cs +++ b/src/CatLib.Core.Tests/Support/Container/ContainerHelperTests.cs @@ -9,6 +9,7 @@ * Document: http://catlib.io/ */ +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CatLib.Tests @@ -38,6 +39,14 @@ public void MakeTService() Assert.AreSame(this, obj); } + [TestMethod] + public void MakeTypeService() + { + var container = MakeContainer(); + var obj = container.Make(typeof(ContainerHelperTests)); + Assert.AreSame(this, obj); + } + /// /// 以单例形式绑定 /// @@ -110,11 +119,193 @@ public void TestRelease() var container = MakeContainer(); var obj = new TestClassService(); container.Instance(obj); + container.OnFindType((str) => + { + return Type.GetType(str); + }); Assert.AreSame(obj, container.Make()); container.Release(); // 因为被释放后容器会容器会自动推测出所需类的实例 - Assert.AreNotSame(obj, container.Make()); + Assert.AreSame(obj.GetType(), container.Make().GetType()); + } + + [TestMethod] + public void TestBindIf() + { + var app = new Application(); + IBindData bindData; + Assert.AreEqual(true, App.BindIf("TestBind", (c, p) => 1, out bindData)); + Assert.AreEqual(false, App.BindIf("TestBind", (c, p) => 2, out bindData)); + Assert.AreEqual(1, app["TestBind"]); + + Assert.AreEqual(true, App.BindIf(out bindData)); + Assert.AreEqual(typeof(object), app.Make().GetType()); + + Assert.AreEqual(true, App.BindIf((c,p) => 100, out bindData)); + Assert.AreEqual(true, App.BindIf(() => 100, out bindData)); + Assert.AreEqual(100, App.Make()); + Assert.AreEqual(true, App.BindIf(out bindData)); + Assert.AreEqual(false, App.BindIf(out bindData)); + + Assert.AreEqual(typeof(double), App.Make(App.Type2Service(typeof(float))).GetType()); + } + + [TestMethod] + public void TestSingletonIf() + { + var testObject = new object(); + var testObject2 = new object(); + var app = new Application(); + IBindData bindData; + Assert.AreEqual(true, App.SingletonIf("TestBind", (c, p) => new object(), out bindData)); + + var makeObject = app["TestBind"]; + Assert.AreEqual(false, App.SingletonIf("TestBind", (c, p) => testObject2, out bindData)); + Assert.AreSame(testObject.GetType(), makeObject.GetType()); + Assert.AreSame(makeObject, app["TestBind"]); + + Assert.AreEqual(true, App.SingletonIf(out bindData)); + Assert.AreEqual(typeof(object), app.Make().GetType()); + + Assert.AreEqual(true, App.SingletonIf((c, p) => 100, out bindData)); + Assert.AreEqual(true, App.SingletonIf(() => 100, out bindData)); + Assert.AreEqual(100, App.Make()); + Assert.AreEqual(true, App.SingletonIf(out bindData)); + Assert.AreEqual(false, App.SingletonIf(out bindData)); + + Assert.AreEqual(typeof(double), App.Make(App.Type2Service(typeof(float))).GetType()); + } + + [TestMethod] + public void TestGetBind() + { + var container = new Container(); + var bind = container.Bind(() => "helloworld"); + + Assert.AreEqual("helloworld", container.Make(container.Type2Service())); + Assert.AreEqual(true, container.HasBind()); + Assert.AreSame(bind, container.GetBind()); + } + + [TestMethod] + public void TestCanMake() + { + var container = new Container(); + + Assert.AreEqual(false, container.CanMake()); + container.Bind(() => "helloworld"); + Assert.AreEqual(true, container.CanMake()); + } + + [TestMethod] + public void TestIsStatic() + { + var container = new Container(); + container.Bind(() => "helloworld"); + Assert.AreEqual(false, container.IsStatic()); + container.Unbind(); + container.Singleton(() => "helloworld"); + Assert.AreEqual("helloworld", container.Make(container.Type2Service())); + Assert.AreEqual(true, container.IsStatic()); + } + + [TestMethod] + public void TestIsAlias() + { + var container = new Container(); + container.Bind(() => "helloworld").Alias(); + Assert.AreEqual(false, container.IsAlias()); + Assert.AreEqual(true, container.IsAlias()); + } + + [TestMethod] + public void TestGetService() + { + var container = new Container(); + Assert.AreEqual(container.Type2Service(typeof(string)), container.Type2Service()); + } + + public class TestWatchCLass + { + public int value; + + public IContainer container; + + public void OnChange(int instance, IContainer container) + { + value = instance; + this.container = container; + } + } + + public interface IWatchTest + { + int getValue(); + } + + public class TestData : IWatchTest + { + private int val; + public TestData(int val) + { + this.val = val; + } + public int getValue() + { + return val; + } + } + + [TestMethod] + public void TestWatch() + { + var container = new Container(); + container.Instance(container); + + var cls = new TestWatchCLass(); + container.Watch(cls, "OnChange"); + container.Instance(100); + container.Instance(200); + + Assert.AreEqual(200, cls.value); + Assert.AreSame(container, cls.container); + } + + [TestMethod] + public void TestWatchLambda() + { + var container = new Container(); + container.Instance(container); + + var isCall = false; + container.Watch((val) => + { + isCall = true; + Assert.AreEqual(200, val.getValue()); + }); + container.Instance(new TestData(100)); + container.Instance(new TestData(200)); + + Assert.AreEqual(true, isCall); + Assert.AreEqual(true, isCall); + } + + [TestMethod] + public void TestWatchLambdaNoParam() + { + var container = new Container(); + container.Instance(container); + + var isCall = false; + container.Watch(() => + { + isCall = true; + }); + container.Instance(100); + container.Instance(200); + + Assert.AreEqual(true, isCall); } /// diff --git a/src/CatLib.Core.Tests/Support/Container/ContainerPerformanceTests.cs b/src/CatLib.Core.Tests/Support/Container/ContainerPerformanceTests.cs new file mode 100644 index 0000000..609e33a --- /dev/null +++ b/src/CatLib.Core.Tests/Support/Container/ContainerPerformanceTests.cs @@ -0,0 +1,220 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CatLib.Tests.Stl +{ + /// + /// 容器性能测试 + /// + [TestClass] + public class ContainerPerformanceTests + { + public void Watch(string name ,Action action, int count = 1) + { + var sw = new Stopwatch(); + sw.Start(); + while (count-- > 0) + { + action(); + } + sw.Stop(); + Console.WriteLine("["+ name + "]执行花费{0}ms.", sw.Elapsed.TotalMilliseconds); + } + + public class TestSerializeClass + { + + } + + [TestMethod] + public void TestCreateInstance() + { + Watch("CreateInstance()", () => + { + Activator.CreateInstance(typeof(TestSerializeClass)); + }, 1000000); + + Watch("CreateInstance(null)",() => + { + Activator.CreateInstance(typeof(TestSerializeClass), null); + }, 1000000); + + Watch("CreateInstance(object[])", () => + { + Activator.CreateInstance(typeof(TestSerializeClass), new object[]{}); + }, 1000000); + } + + public class TestMakeHandClass + { + public TestMakeHandClass(TestSerializeClass cls) + { + + } + } + + public class TestMakeClass : ITestMakeClass + { + public TestMakeClass(TestSerializeClass cls) + { + + } + } + + public class TestMakeClass2 + { + public TestMakeClass2(TestSerializeClass cls) + { + + } + } + + public class TestMakeNullParamsClass : ITestMakeNullParamsClass + { + public TestMakeNullParamsClass() + { + + } + } + + public interface ITestMakeClass { } + public interface ITestMakeNullParamsClass { } + + [TestMethod] + public void TestSingleMake() + { + var container = new Container(); + container.Singleton((_, __) => new TestMakeHandClass(null)); + container.Singleton(); + container.Singleton(); + container.Singleton().Alias(); + + Watch("TestSingleMake(非反射) 1000000次", () => + { + container.Make(); + }, 1000000); + + Watch("TestSingleMake(反射,依赖注入) 1000000次", () => + { + container.Make(); + }, 1000000); + + Watch("TestSingleMake(反射,无注入) 1000000次", () => + { + container.Make(); + }, 1000000); + } + + [TestMethod] + public void TestBindMake() + { + var container = new Container(); + container.Bind((_, __) => new TestMakeHandClass(null)); + container.Singleton(); + container.Bind().Alias(); + container.Bind().Alias(); + + Watch("TestBindMake(非反射) 1000000次", () => + { + container.Make(); + }, 1000000); + + Watch("TestBindMake(反射,依赖注入) 1000000次", () => + { + container.Make(); + }, 1000000); + + Watch("TestBindMake(反射,无注入) 1000000次", () => + { + container.Make(); + }, 1000000); + } + + public class TestMakeClassFacade : Facade + { + + } + + public class TestMakeClassNoParamsFacade : Facade + { + + } + + public abstract class OriginalFacade where TInterface : new() + { + private static TInterface instance; + /// + /// 门面实例 + /// + public static TInterface Instance + { + get + { + if (instance != null) + { + return instance; + } + + return instance = new TInterface(); + } + } + } + + public class TestOriginalFacade : OriginalFacade + { + } + + [TestMethod] + public void TestOriginalFacadeSpeed() + { + Watch("TestOriginalFacadeSpeed() 1000000次", () => + { + var obj = TestOriginalFacade.Instance; + }, 1000000); + } + + [TestMethod] + public void TestSingletonFacade() + { + var container = new Application(); + container.Singleton(); + container.Singleton().Alias(); + + Watch("TestSingletonFacade() 1000000次", () => + { + var obj = TestMakeClassFacade.Instance; + }, 1000000); + } + + [TestMethod] + public void TestBindFacade() + { + var container = new Application(); + container.Singleton(); + container.Bind().Alias(); + container.Bind().Alias(); + + Watch("TestBindFacade() 1000000次", () => + { + var obj = TestMakeClassFacade.Instance; + }, 1000000); + + Watch("TestBindFacade(无参数) 1000000次", () => + { + var obj = TestMakeClassNoParamsFacade.Instance; + }, 1000000); + } + } +} diff --git a/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs b/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs index 1fc373d..0af74b7 100644 --- a/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs +++ b/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs @@ -85,6 +85,30 @@ public void CanMakeWithTaged() }); } + [TestMethod] + public void TestUnbind() + { + var container = MakeContainer(); + container.Bind("TestService1", (app, param) => "hello"); + container.Bind("TestService2", (app, param) => "world").Alias(); + + container.Unbind("TestService1"); + container.Unbind(); + + ExceptionAssert.Throws(() => + { + container.Make("TestService1"); + }); + + ExceptionAssert.Throws(() => + { + container.Make("TestService2"); + }); + + container.Bind("TestService2", (app, param) => "hello"); + Assert.AreEqual("hello", container["TestService2"]); + } + /// /// 测试不存在的Tag /// @@ -108,7 +132,12 @@ public void MergeTag() container.Tag("hello", "world"); container.Tag("hello", "world2"); + container.Bind("world", (c, p) => "hello"); + container.Bind("world2", (c, p) => "world"); + Assert.AreEqual(2, container.Tagged("hello").Length); + Assert.AreEqual("hello", container.Tagged("hello")[0]); + Assert.AreEqual("world", container.Tagged("hello")[1]); } /// @@ -127,6 +156,29 @@ public void NullTagService() #endregion #region Bind + /// + /// 测试无法被绑定的类型 + /// + [TestMethod] + public void TestBindUnableBuilt() + { + var container = MakeContainer(); + + IBindData binder; + Assert.AreEqual(false, container.BindIf(out binder)); + + var isError = false; + try + { + container.Bind(); + } + catch (RuntimeException) + { + isError = true; + } + Assert.AreEqual(true, isError); + } + /// /// 是否能够进行如果不存在则绑定的操作 /// @@ -134,10 +186,13 @@ public void NullTagService() public void CanBindIf() { var container = MakeContainer(); - var bind = container.BindIf("CanBindIf", (cont, param) => "Hello", true); - var bind2 = container.BindIf("CanBindIf", (cont, param) => "World", false); + IBindData bind1, bind2; + var result1 = container.BindIf("CanBindIf", (cont, param) => "Hello", true, out bind1); + var result2 = container.BindIf("CanBindIf", (cont, param) => "World", false, out bind2); - Assert.AreSame(bind, bind2); + Assert.AreSame(bind1, bind2); + Assert.AreEqual(true, result1); + Assert.AreEqual(false, result2); } /// @@ -147,10 +202,13 @@ public void CanBindIf() public void CanBindIfByType() { var container = MakeContainer(); - var bind = container.BindIf("CanBindIf", typeof(ContainerTest), true); - var bind2 = container.BindIf("CanBindIf", typeof(ContainerTest), false); + IBindData bind1, bind2; + var result1 = container.BindIf("CanBindIf", typeof(ContainerTest), true, out bind1); + var result2 = container.BindIf("CanBindIf", typeof(ContainerTest), false, out bind2); - Assert.AreSame(bind, bind2); + Assert.AreSame(bind1, bind2); + Assert.AreEqual(true, result1); + Assert.AreEqual(false, result2); } /// @@ -441,6 +499,114 @@ public void CheckLoopDependency() }); } + /// + /// 调用方法注入测试 + /// + [TestMethod] + public void CheckDelegateCall() + { + var container = MakeContainer(); + container.Instance(container); + + container.Call((Container cls) => + { + Assert.AreNotEqual(null, cls); + }); + + container.Call((Container cls1, Container cls2) => + { + Assert.AreNotEqual(null, cls1); + Assert.AreNotEqual(null, cls2); + }); + + container.Call((Container cls1, Container cls2, Container cls3) => + { + Assert.AreNotEqual(null, cls1); + Assert.AreNotEqual(null, cls2); + Assert.AreNotEqual(null, cls3); + }); + + container.Call((Container cls1, Container cls2, Container cls3, Container cls4) => + { + Assert.AreNotEqual(null, cls1); + Assert.AreNotEqual(null, cls2); + Assert.AreNotEqual(null, cls3); + Assert.AreNotEqual(null, cls4); + + Assert.AreSame(cls1, cls4); + }); + } + + [TestMethod] + public void CheckWrapCall() + { + var container = MakeContainer(); + container.Instance(container); + + var callCount = 0; + var wrap = container.Wrap((Container cls) => + { + Assert.AreNotEqual(null, cls); + callCount++; + }); + wrap.Invoke(); + + wrap = container.Wrap((Container cls1, Container cls2) => + { + Assert.AreNotEqual(null, cls1); + Assert.AreNotEqual(null, cls2); + callCount++; + }); + wrap.Invoke(); + + wrap = container.Wrap((Container cls1, Container cls2, Container cls3) => + { + Assert.AreNotEqual(null, cls1); + Assert.AreNotEqual(null, cls2); + Assert.AreNotEqual(null, cls3); + callCount++; + }); + wrap.Invoke(); + + wrap = container.Wrap((Container cls1, Container cls2, Container cls3, Container cls4) => + { + Assert.AreNotEqual(null, cls1); + Assert.AreNotEqual(null, cls2); + Assert.AreNotEqual(null, cls3); + Assert.AreNotEqual(null, cls4); + + Assert.AreSame(cls1, cls4); + callCount++; + }); + wrap.Invoke(); + + Assert.AreEqual(4, callCount); + } + + [TestMethod] + public void TestFactory() + { + var container = MakeContainer(); + container.Instance(container); + container.Instance("hello", 123); + + var fac = container.Factory(123); + Assert.AreEqual(container.Make(), fac.Invoke()); + var fac2 = container.Factory("hello", 333); + Assert.AreEqual(123, fac2.Invoke()); + } + + [TestMethod] + public void TestIsAlias() + { + var container = MakeContainer(); + container.Instance(container); + container.Alias("123", container.Type2Service()); + + Assert.AreEqual(true, container.IsAlias("123")); + Assert.AreEqual(false, container.IsAlias(container.Type2Service(typeof(Container)))); + } + /// /// 可以调用方法 /// @@ -464,7 +630,7 @@ public void CanCallMethod() [TestMethod] public void CanCallMethodNoParam() { - var container = MakeContainer(); + var container = MakeContainer() as IContainer; container.Bind(); var cls = new CallTestClass(); @@ -497,8 +663,20 @@ public void CheckIllegalCallMethod() public void TestContainerCallWithNullParams() { var container = MakeContainer(); + container.Instance("@num", 777); var result = container.Call(this, "TestContainerCall", null); - Assert.AreEqual(0, result); + Assert.AreEqual(777, result); + } + + [TestMethod] + public void TestContainerCallWithErrorParams() + { + var container = MakeContainer(); + container.Instance("@num", "helloworld"); + ExceptionAssert.Throws(() => + { + container.Call(this, "TestContainerCall", null); + }); } /// @@ -511,18 +689,54 @@ public void CheckIllegalCallMethodParam() container.Bind(); var cls = new CallTestClass(); - ExceptionAssert.Throws(() => - { - container.Call(cls, "GetNumber", "illegal param"); - }); - + Assert.AreEqual(2, container.Call(cls, "GetNumber", "illegal param")); var result = container.Call(cls, "GetNumber", null); Assert.AreEqual(2, result); } + + [TestMethod] + public void TestOverflowParamNum() + { + var container = MakeContainer(); + var cls = new CallTestClass(); + + var isThrow = false; + try + { + container.Call(cls, "GetNumber", new object[256]); + } + catch (Exception ex) + { + isThrow = true; + } + + Assert.AreEqual(true, isThrow); + } + + class SimpleTestClass1 { } + class SimpleTestClass2 { } + + [TestMethod] + public void TestLooseParameters() + { + var container = MakeContainer(); + container.Bind(); + container.Bind(); + + var objOut = new object(); + var call = container.Wrap((object[] obj, SimpleTestClass1 cls1, SimpleTestClass2 cls2) => + { + Assert.AreSame(objOut, obj[0]); + Assert.AreNotEqual(null, cls1); + Assert.AreNotEqual(null, cls2); + Assert.AreEqual((long)100, obj[1]); + }, objOut, (long)100); + + call.Invoke(); + } #endregion #region Make - public class MakeTestClass { private readonly MakeTestClassDependency dependency; @@ -530,13 +744,13 @@ public class MakeTestClass [Inject] public MakeTestClassDependency Dependency { get; set; } - [Inject(Required = true)] + [Inject] public MakeTestClassDependency DependencyRequired { get; set; } [Inject("AliasName")] public MakeTestClassDependency2 DependencyAlias { get; set; } - [Inject("AliasNameRequired", Required = true)] + [Inject("AliasNameRequired")] public MakeTestClassDependency DependencyAliasRequired { get; set; } public MakeTestClass(MakeTestClassDependency dependency) @@ -598,9 +812,10 @@ public void MakeNoClassAttrInject() { var container = MakeContainer(); container.Bind(); + container.Bind("@Time", (c, p) => 100, false); var result = container.Make(); - Assert.AreEqual(0, result.Time); + Assert.AreEqual(100, result.Time); } /// @@ -618,6 +833,7 @@ public void MakeNoBindType() }); container.Bind().Alias("AliasNameRequired"); + container.Bind().Alias("AliasName"); var result = container.Make(); Assert.AreNotEqual(null, result); @@ -653,11 +869,12 @@ public void MakeNotClassConstructor() var container = MakeContainer(); container.Bind(); container.Bind(); + container.Instance("@i", 77); var result = container.Make(); - Assert.AreEqual(0, result.I); + Assert.AreEqual(77, result.I); Assert.AreNotEqual(null, result.Dependency); - var result2 = container.MakeWith(100); + var result2 = container.Make(100); Assert.AreEqual(100, result2.I); Assert.AreNotEqual(null, result2.Dependency); } @@ -671,12 +888,13 @@ public void CanMake() var container = MakeContainer(); container.Bind(); container.Bind().Alias("AliasNameRequired"); + container.Bind().Alias("AliasName"); var result = container.Make(); Assert.AreEqual(typeof(MakeTestClass), result.GetType()); var dep = new MakeTestClassDependency(); - var result2 = container.MakeWith(dep); + var result2 = container.Make(dep); Assert.AreEqual(typeof(MakeTestClass), result2.GetType()); var result3 = container[container.Type2Service(typeof(MakeTestClass))] as MakeTestClass; @@ -745,6 +963,7 @@ public void CanMakeNormalBind() var container = MakeContainer(); container.Bind(); container.Bind().Alias("AliasNameRequired"); + container.Bind().Alias("AliasName"); var result1 = container.Make(); var result2 = container.Make(); @@ -753,7 +972,7 @@ public void CanMakeNormalBind() Assert.AreNotSame(result1.Dependency, result1.DependencyRequired); Assert.AreNotSame(null, result1.DependencyRequired); Assert.AreNotSame(null, result1.DependencyAliasRequired); - Assert.AreSame(null, result1.DependencyAlias); + Assert.AreNotEqual(null, result1.DependencyAlias); } /// @@ -861,11 +1080,154 @@ public void CanParamUseInjectAttr() cls = container.Make(); Assert.AreEqual("hello", cls.GetMsg()); - subBind.UnBind(); + subBind.Unbind(); cls = container.Make(); Assert.AreEqual("hello", cls.GetMsg()); } + public class TestMakeBasePrimitive + { + [Inject] + public int Value { get; set; } + } + + [TestMethod] + public void TestUnresolvablePrimitiveAttr() + { + var container = MakeContainer(); + container.Bind(); + + ExceptionAssert.Throws(() => + { + container.Make(); + }); + } + + public class TestMakeBasePrimitiveConstructor + { + public TestMakeBasePrimitiveConstructor(int value) + { + + } + } + + [TestMethod] + public void TestUnresolvablePrimitiveConstructor() + { + var container = MakeContainer(); + container.Bind(); + ExceptionAssert.Throws(() => + { + container.Make(); + }); + } + + public class TestOptionalPrimitiveClass + { + public TestOptionalPrimitiveClass(int value = 100) + { + Assert.AreEqual(100, value); + } + } + + public class SupportNullContainer : Container + { + public string[] GetStack() + { + return BuildStack.ToArray(); + } + + public object[][] GetUserParams() + { + return UserParamsStack.ToArray(); + } + + protected override void GuardResolveInstance(object instance, string makeService) + { + + } + } + + [TestMethod] + public void TestSupportNullValueContainer() + { + var container = new SupportNullContainer() as IContainer; + container.Bind("null", (c, p) => null); + + Assert.AreEqual(null, container.Make("null")); + } + + [TestMethod] + public void TestGetStack() + { + var container = new SupportNullContainer(); + container.Bind("null", (c, p) => + { + Assert.AreEqual(1, container.GetStack().Length); + Assert.AreEqual(1, container.GetUserParams().Length); + Assert.AreEqual(3, container.GetUserParams()[0].Length); + return null; + }); + + Assert.AreEqual(null, container.Make("null", "123", "hello", 12333)); + + Assert.AreEqual(0, container.GetStack().Length); + Assert.AreEqual(0, container.GetUserParams().Length); + } + + public class TestInjectNullClass + { + public TestInjectNullClass(TestMakeBasePrimitiveConstructor cls) + { + Assert.AreEqual(null, cls); + } + } + + [TestMethod] + public void TestInjectNull() + { + var container = new SupportNullContainer() as IContainer; + container.Bind(); + + container.Make(); + } + + public class TestDefaultValueClass + { + public TestDefaultValueClass(SupportNullContainer container = null) + { + Assert.AreEqual(null, container); + } + } + + [TestMethod] + public void TestDefaultValue() + { + var container = new Container(); + container.Bind(); + container.Make(); + } + + [TestMethod] + public void TestAllFalseFindType() + { + var container = new Container(); + + container.OnFindType((str) => null); + + ExceptionAssert.Throws(() => + { + container.Make(); + }); + } + + [TestMethod] + public void TestOptionalPrimitive() + { + var container = MakeContainer(); + container.Bind(); + container.Make(); + } /// /// 参数注入是必须的 @@ -874,7 +1236,7 @@ public class TestMakeParamInjectAttrRequiredClass { private IMsg msg; public TestMakeParamInjectAttrRequiredClass( - [Inject(Required = true)]IMsg msg) + IMsg msg) { this.msg = msg; } @@ -972,9 +1334,9 @@ public void CanMakeGenericInject() Assert.AreNotEqual(null, result.Cls); Assert.AreEqual(typeof(string).ToString(), result.Cls.GetMsg()); - container.Bind>((app, param) => null); + container.Bind>((app, param) => new GenericClass()); result = container.Make(); - Assert.AreEqual(null, result.Cls); + Assert.AreNotEqual(null, result.Cls); } @@ -1044,8 +1406,11 @@ public void InvalidClassNew() { return Type.GetType(str); }); - var result = container.Make(); - Assert.AreEqual(null, result); + + ExceptionAssert.Throws(() => + { + container.Make(); + }); } /// @@ -1226,56 +1591,223 @@ public void CanInstanceWithRelease() } Assert.Fail(); } + + public class TestDisposableClass : IDisposable + { + public bool isDispose; + public void Dispose() + { + isDispose = true; + } + } + + [TestMethod] + public void TestDisposableRelease() + { + var container = MakeContainer(); + container.Singleton(); + var cls = container.Make(); + container.Release(); + Assert.AreEqual(true, cls.isDispose); + } #endregion - /// - /// 已存在的静态对象在注册新的OnResolving时会自动触发 - /// [TestMethod] - public void OnResolvingExistsObject() + public void TestOneWatch() { var container = MakeContainer(); - var data = new List { "hello world" }; - container.Instance("TestInstance", data); - var isCall = false; - container.OnResolving((bind, obj) => + var b = container.Bind((c, p) => 123); + var obj = container.Make(); + Assert.AreEqual(123, obj); + + b.Unbind(); + + object ins1 = null; + container.Watch((instance) => { - isCall = true; - Assert.AreSame(data, obj); - return obj; + ins1 = instance; }); + container.Bind((c, p) => new object()); - Assert.AreEqual(true, isCall); + Assert.AreNotEqual(null, ins1); } - /// - /// 测试释放所有静态服务 - /// [TestMethod] - public void TestReleaseAllStaticService() + public void TestNullBindWatch() { var container = MakeContainer(); - var data = new List { "hello world" }; - var isCallTest = false; - container.Singleton("Test", (c, p) => { return "Test1"; }).OnRelease((bind, o) => { isCallTest = true; }); - container.Instance("TestInstance2", data); + container.Instance(123); - Assert.AreEqual("Test1", container.Make("Test")); - - container.Flush(); + object ins1 = null, ins2 = null; + container.Watch((instance) => + { + ins1 = instance; + }); + container.Watch((instance) => + { + ins2 = instance; + }); + var obj = new object(); + container.Instance(obj); - Assert.AreEqual(true, isCallTest); - Assert.AreEqual(null, container.Make("TestInstance2")); - Assert.AreEqual(null, container.Make("Test")); + Assert.AreSame(obj, ins1); + Assert.AreEqual(obj, ins1); + Assert.AreSame(obj, ins2); + Assert.AreEqual(obj, ins2); } [TestMethod] - public void TestSameAliaseServiceName() + public void TestBindWatch() { var container = MakeContainer(); - ExceptionAssert.Throws(() => + var b = container.Bind((c, p) => 123); + var obj = container.Make(); + Assert.AreEqual(123, obj); + + b.Unbind(); + + object ins1 = null,ins2 = null; + container.Watch((instance) => + { + ins1 = instance; + }); + container.Watch((instance) => + { + ins2 = instance; + }); + container.Bind((c, p) => new object()); + + Assert.AreNotSame(ins1, ins2); + } + + [TestMethod] + public void TestSingletonWatch() + { + var container = MakeContainer(); + + var b = container.Singleton((c, p) => 123); + var obj = container.Make(); + Assert.AreEqual(123, obj); + + b.Unbind(); + + object ins1 = null, ins2 = null; + container.Watch((instance) => + { + ins1 = instance; + }); + container.Watch((instance) => + { + ins2 = instance; + }); + container.Singleton((c, p) => new object()); + + Assert.AreSame(ins1, ins2); + } + + [TestMethod] + public void TestNullFlash() + { + var container = MakeContainer(); + container.Flash(() => + { + }, null); + + // no throw error is success + } + + [TestMethod] + public void TestEmptyFlash() + { + var container = MakeContainer(); + container.Flash(() => + { + }, new KeyValuePair[] { }); + + // no throw error is success + } + + [TestMethod] + public void TestFlashRecursive() + { + var container = MakeContainer(); + + var call = 0; + container.Flash(() => + { + call++; + Assert.AreEqual(1, container.Make("hello")); + Assert.AreEqual(2, container.Make("world")); + container.Flash(() => + { + call++; + Assert.AreEqual(10, container.Make("hello")); + Assert.AreEqual(2, container.Make("world")); + }, new KeyValuePair("hello", 10)); + Assert.AreEqual(1, container.Make("hello")); + Assert.AreEqual(2, container.Make("world")); + },new KeyValuePair("hello", 1) + , new KeyValuePair("world", 2)); + + Assert.AreEqual(false, container.HasInstance("hello")); + Assert.AreEqual(false, container.HasInstance("world")); + Assert.AreEqual(2, call); + } + + [TestMethod] + public void OnResolvingExistsObject() + { + var container = MakeContainer(); + var data = new List { "hello world" }; + container.Instance("TestInstance", data); + + var isCall = false; + container.OnResolving((bind, obj) => + { + isCall = true; + return obj; + }); + + Assert.AreEqual(false, isCall); + } + + /// + /// 测试释放所有静态服务 + /// + [TestMethod] + public void TestReleaseAllStaticService() + { + var container = MakeContainer(); + var data = new List { "hello world" }; + var isCallTest = false; + container.Singleton("Test", (c, p) => { return "Test1"; }).OnRelease((bind, o) => { isCallTest = true; }); + container.Instance("TestInstance2", data); + + Assert.AreEqual("Test1", container.Make("Test")); + + container.Flush(); + + Assert.AreEqual(true, isCallTest); + + ExceptionAssert.Throws(() => + { + container.Make("TestInstance2"); + }); + + ExceptionAssert.Throws(() => + { + container.Make("Test"); + }); + } + + [TestMethod] + public void TestSameAliaseServiceName() + { + var container = MakeContainer(); + + ExceptionAssert.Throws(() => { container.Singleton().Alias(); }); @@ -1293,7 +1825,7 @@ public void TestMakeWithParams() { var container = MakeContainer(); container.Bind(); - Assert.AreEqual(typeof(TestParamsMakeClass), container.MakeWith(null).GetType()); + Assert.AreEqual(typeof(TestParamsMakeClass), container.Make(null).GetType()); } [TestMethod] @@ -1308,35 +1840,16 @@ public void TestBaseStructChange() public void TestBaseStructChangeInvalid() { var container = new Container(); - - var isThrow = false; - try - { - container.Call(this, "TestContainerCall", "100000000000000000000"); - } - catch (RuntimeException) - { - isThrow = true; - } - - Assert.AreEqual(true, isThrow); + container.Instance(10000); + Assert.AreEqual(10000, container.Call(this, "TestContainerCall", "100000000000000000000")); } [TestMethod] public void TestFormatException() { var container = new Container(); - - var isThrow = false; - try - { - container.Call(this, "TestContainerCall", new ContainerTest()); - } - catch (RuntimeException) - { - isThrow = true; - } - Assert.AreEqual(true, isThrow); + container.Instance("@num", 10); + Assert.AreEqual(10, container.Call(this, "TestContainerCall", new ContainerTest())); } internal class TestNoConstructorAccessClass @@ -1354,7 +1867,8 @@ public void TestNoConstructorAccessClassFunction() try { container.Make(); - }catch(RuntimeException ex) + } + catch (RuntimeException ex) { isThrow = ex.InnerException.GetType() == typeof(MissingMethodException); } @@ -1390,6 +1904,209 @@ public void TestConstructorExceptionFunction() Assert.AreEqual(true, isException); } + private class ParamsTypeInjectTest + { + public int num1; + public long num2; + public string str; + + public ParamsTypeInjectTest(int num1, long num2,string str) + { + this.num1 = num1; + this.num2 = num2; + this.str = str; + } + } + + [TestMethod] + public void TestParamsUserParams() + { + var container = MakeContainer(); + container.Bind(); + + var result = container.Make(new Params + { + {"num2", 100}, + {"num1", 50}, + {"str", "helloworld"}, + }, 100, 200, "dog"); + + Assert.AreEqual(50, result.num1); + Assert.AreEqual(100, result.num2); + Assert.AreEqual("helloworld", result.str); + } + + [TestMethod] + public void TestMultParamsUserParams() + { + var container = MakeContainer(); + container.Bind(); + + var result = container.Make(new Params + { + {"num2", 100}, + {"num1", 50}, + }, 100, new Params + { + {"num2", 500}, + {"num1", 4000}, + {"str", "helloworld"}, + }, 200, "dog"); + + Assert.AreEqual(50, result.num1); + Assert.AreEqual(100, result.num2); + Assert.AreEqual("helloworld", result.str); + } + + [TestMethod] + public void TestParamsUserParamsThrowError() + { + var container = MakeContainer(); + container.Bind(); + + ExceptionAssert.Throws(() => + { + var result = container.Make(new Params + { + {"num2", 100}, + {"num1", "helloworld"}, + {"str", "helloworld"}, + }); + }); + } + + private class ContainerReplaceThrow : Container + { + // 测试由于开发者写错代码导致的bug是否被正确抛出异常 + protected override Func GetParamsMatcher(ref object[] userParams) + { + return (param) => + { + return 200; + }; + } + } + + [TestMethod] + public void TestReplaceContainerParamsUserParamsThrowError() + { + var container = new ContainerReplaceThrow(); + container.Bind(); + + ExceptionAssert.Throws(() => + { + container.Make(new Params + { + {"num2", 100}, + {"num1", "helloworld"}, + {"str", "helloworld"}, + }); + }); + } + + public class TestResloveAttrClassSpeculationService + { + [Inject] + public RuntimeException rex { get; set; } + + public UnresolvableException ex; + public TestResloveAttrClassSpeculationService(UnresolvableException ex) + { + this.ex = ex; + } + } + + [TestMethod] + public void TestResloveAttrClassSpeculationServiceFunc() + { + var container = new Container(); + container.Bind(); + container.Instance("@ex", new UnresolvableException()); + container.Instance("@rex", new UnresolvableException()); + var cls = container.Make(); + + Assert.AreSame(container.Make("@ex"), cls.ex); + } + + [TestMethod] + public void TestResloveAttrClassSpeculationServiceAttrs() + { + var container = new Container(); + container.Bind(); + container.Instance("@ex", new UnresolvableException()); + + ExceptionAssert.Throws(() => + { + container.Make(); + }); + } + + public class VariantModel : IVariant + { + public int num; + public VariantModel(int num) + { + this.num = num; + if (num == 0) + { + throw new Exception(); + } + } + } + + public class VariantFather + { + public long code; + public VariantModel model; + public VariantFather(long code, VariantModel model) + { + this.code = code; + this.model = model; + } + } + + /// + /// 测试类型变换 + /// + [TestMethod] + public void TestVariant() + { + var container = new Container(); + container.Bind(); + container.Bind(); + + var cls = container.Make(10, 20); + + Assert.AreEqual(10, cls.code); + Assert.AreEqual(20, cls.model.num); + } + + [TestMethod] + public void TestVariantThrowError() + { + var container = new Container(); + container.Bind(); + container.Bind(); + + ExceptionAssert.Throws(() => + { + container.Make(10, 0); + }); + } + + [TestMethod] + public void TestNullFromParams() + { + var container = new Container(); + container.Bind(); + container.Bind(); + + ExceptionAssert.Throws(() => + { + container.Make(10, new Params {{"model", null}}); + }); + } + /// /// 测试基础容器调用 /// @@ -1399,6 +2116,170 @@ public int TestContainerCall(int num) return num; } + #region Rebound + [TestMethod] + public void TestOnRebound() + { + var container = new Container(); + var callRebound = false; + container.OnRebound("TestService", (instance) => + { + Assert.AreEqual(300, instance); + callRebound = true; + }); + + container.Bind("TestService", (c, p) => 100).Unbind(); + var bind = container.Bind("TestService", (c, p) => 200); + container.Make("TestService"); + bind.Unbind(); + container.Bind("TestService", (c, p) => 300); + + Assert.AreEqual(true, callRebound); + } + + [TestMethod] + public void TestOnReboundWithInstance() + { + var container = new Container(); + var callRebound = false; + container.OnRebound("TestService", (instance) => + { + Assert.AreEqual(300, instance); + callRebound = true; + }); + + container.Instance("TestService", 100); + container.Instance("TestService", 300); + + Assert.AreEqual(true, callRebound); + } + + public class TestWatchCLass + { + public int value; + + public IContainer container; + + public void OnChange(int instance, IContainer container) + { + value = instance; + this.container = container; + } + } + + [TestMethod] + public void TestWatch() + { + var container = new Container(); + + container.Instance(container); + var cls = new TestWatchCLass(); + container.Watch("WatchService", cls, "OnChange"); + container.Instance("WatchService", 100); + container.Instance("WatchService", 200); + + Assert.AreEqual(200, cls.value); + Assert.AreSame(container, cls.container); + } + + [TestMethod] + public void TestInstanceAndDecorator() + { + var container = new Container(); + var oldObject = new object(); + object newObject = null; + container.OnResolving((bindData, obj) => + { + return newObject = new object(); + }); + + container.Instance("Hello", oldObject); + + Assert.AreSame(newObject, container["Hello"]); + } + + [TestMethod] + public void TestOccupiedKeyInstance() + { + var container = new Container(); + container.Instance(null); + var cls = new TestWatchCLass(); + container.Watch("WatchService", cls, "OnChange"); + container.Instance("WatchService", 100); + + var isThrow = false; + try + { + container.Instance("WatchService", 200); + } + catch (RuntimeException) + { + isThrow = true; + } + + Assert.AreEqual(true, isThrow); + } + + [TestMethod] + public void TestFlashOnBind() + { + var container = new Application(); + container.Bind((c, p) => 100); + + ExceptionAssert.Throws(() => + { + App.Flash(() => + { + + }, App.Type2Service(typeof(IBindData)), 200); + }); + } + + [TestMethod] + public void TestHasInstance() + { + var container = new Application(); + container.Instance(container); + + Assert.AreEqual(true, container.HasInstance()); + Assert.AreEqual(false, container.HasInstance()); + } + + [TestMethod] + public void TestIsResolved() + { + var container = new Application(); + container.Instance(container); + + Assert.AreEqual(true, container.IsResolved()); + Assert.AreEqual(false, container.IsResolved()); + } + + [TestMethod] + public void TestFlushAndInstance() + { + var container = new Application(); + container.Instance(container); + + container.OnRelease((_, __) => + { + container.Instance(container); + }); + + var isError = false; + try + { + container.Flush(); + } + catch (RuntimeException) + { + isError = true; + } + + Assert.AreEqual(true, isError); + } + #endregion + /// /// 生成容器 /// diff --git a/src/CatLib.Core.Tests/Support/Container/GivenDataTests.cs b/src/CatLib.Core.Tests/Support/Container/GivenDataTests.cs index d90fbe6..c279dea 100644 --- a/src/CatLib.Core.Tests/Support/Container/GivenDataTests.cs +++ b/src/CatLib.Core.Tests/Support/Container/GivenDataTests.cs @@ -28,12 +28,12 @@ public void CanGiven() { var container = new Container(); var bindData = new BindData(container, "CanGiven", (app, param) => "hello world", false); - var givenData = new GivenData(container, bindData); + var givenData = new GivenData(container, bindData); givenData.Needs("needs1"); givenData.Given("hello"); Assert.AreEqual("hello", bindData.GetContextual("needs1")); - givenData = new GivenData(container, bindData); + givenData = new GivenData(container, bindData); givenData.Needs("needs2"); givenData.Given(); Assert.AreEqual(container.Type2Service(typeof(GivenDataTest)), bindData.GetContextual("needs2")); @@ -47,7 +47,7 @@ public void CheckGivenIllegalValue() { var container = new Container(); var bindData = new BindData(container, "CanGiven", (app, param) => "hello world", false); - var givenData = new GivenData(container, bindData); + var givenData = new GivenData(container, bindData); givenData.Needs("needs"); ExceptionAssert.Throws(() => diff --git a/src/CatLib.Core.Tests/Support/Container/MethodContainerTests.cs b/src/CatLib.Core.Tests/Support/Container/MethodContainerTests.cs new file mode 100644 index 0000000..efe424f --- /dev/null +++ b/src/CatLib.Core.Tests/Support/Container/MethodContainerTests.cs @@ -0,0 +1,183 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using CatLib.Tests; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CatLib.Core.Tests.Support.Container +{ + [TestClass] + public class MethodContainerTests + { + [TestMethod] + public void TestBindMethod() + { + new Application(); + App.BindMethod("TestMethod", () => 10); + App.BindMethod("TestMethodContainer", (IContainer container) => container != null); + App.BindMethod("TestMethodContainer2", (IContainer container, IContainer container2) => + { + Assert.AreSame(container, container2); + return container != null; + }); + App.BindMethod("TestMethodInputNum", (IApplication application, IContainer container, int num) => num); + App.BindMethod("TestMethodInputNum2", (IApplication application, IContainer container, int num, float num2) => num2); + + Assert.AreEqual(10, App.Invoke("TestMethod")); + Assert.AreEqual(true, App.Invoke("TestMethodContainer")); + Assert.AreEqual(true, App.Invoke("TestMethodContainer2")); + Assert.AreEqual(1000, App.Invoke("TestMethodInputNum", 1000, 2000)); + Assert.AreEqual((float) 2000, App.Invoke("TestMethodInputNum2", 1000, 2000, 3000)); + } + + [TestMethod] + public void UnBindMethodWithMethodName() + { + new Application(); + App.BindMethod("TestMethod10", () => 10); + App.BindMethod("TestMethod20", () => 20); + + App.UnbindMethod("TestMethod10"); + + ExceptionAssert.Throws(() => + { + App.Invoke("TestMethod10"); + }); + + Assert.AreEqual(20, App.Invoke("TestMethod20")); + } + + public class TestContainerClass + { + public int Func1(IContainer container, int input) + { + return input; + } + + public float Func2(IContainer container, int input, float input2) + { + return input2; + } + } + + [TestMethod] + public void TestBindExcistsMethod() + { + new Application(); + var cls = new TestContainerClass(); + App.BindMethod("Helloworld.Func1", cls); + ExceptionAssert.Throws(() => + { + App.BindMethod("Helloworld.Func1", cls); + }); + } + + public static object TestStaticMethodAction(int num) + { + return num; + } + + [TestMethod] + public void TestStaticMethod() + { + new Application(); + App.BindMethod("echo", TestStaticMethodAction); + Assert.AreEqual(200, App.Invoke("echo", 200)); + } + + [TestMethod] + public void TestUnbindStaticMethod() + { + new Application(); + var bind = App.BindMethod("echo", TestStaticMethodAction); + Assert.AreEqual(100, App.Invoke("echo", 100)); + App.UnbindMethod(bind); + ExceptionAssert.Throws(() => + { + App.Invoke("echo", 200); + }); + } + + [TestMethod] + public void TestUnbindWithObject() + { + new Application(); + var cls = new TestContainerClass(); + App.BindMethod("Helloworld.Func1", cls); + App.BindMethod("Helloworld.Func2", cls); + + var cls2 = new TestContainerClass(); + App.BindMethod("Helloworld2.Func1", cls2); + App.BindMethod("Helloworld2.Func2", cls2); + + App.UnbindMethod(cls); + App.UnbindMethod(cls); // double unbind test + App.UnbindMethod("UnknowMethod"); + + ExceptionAssert.Throws(() => + { + App.Invoke("Helloworld.Func1", 1000, 2000); + }); + + ExceptionAssert.Throws(() => + { + App.Invoke("Helloworld.Func2", 1000, 2000); + }); + + Assert.AreEqual(1000, App.Invoke("Helloworld2.Func1", 1000, 2000)); + Assert.AreEqual((float)2000, App.Invoke("Helloworld2.Func2", 1000, 2000)); + } + + [TestMethod] + public void TestContainerMethodContextual() + { + new Application(); + App.Instance("@input", 1000); + App.Instance("@input2", 2000); + + var cls = new TestContainerClass(); + App.BindMethod("Helloworld.Func1", cls).Needs("@input").Given("@input2"); + App.BindMethod("Helloworld.Func2", cls) + .Needs("@input").Given("@input2") + .Needs("@input2").Given("@input"); ; + + Assert.AreEqual(2000, App.Invoke("Helloworld.Func1")); + Assert.AreEqual((float)1000, App.Invoke("Helloworld.Func2")); + } + + [TestMethod] + public void TestFlush() + { + new Application(); + var cls = new TestContainerClass(); + var bind1 = App.BindMethod("Helloworld.Func1", cls); + var bind2 = App.BindMethod("Helloworld.Func2", cls); + + App.Handler.Flush(); + + ExceptionAssert.Throws(() => + { + App.Invoke("Helloworld.Func1", 1000, 2000); + }); + + ExceptionAssert.Throws(() => + { + App.Invoke("Helloworld.Func2", 1000, 2000); + }); + + App.UnbindMethod(cls); + App.UnbindMethod(cls); // double unbind test + App.UnbindMethod(bind1); + App.UnbindMethod(bind2); + } + } +} diff --git a/src/CatLib.Core.Tests/Support/Dispatcher/DispatcherTests.cs b/src/CatLib.Core.Tests/Support/Dispatcher/DispatcherTests.cs index a9885b0..d8f2463 100644 --- a/src/CatLib.Core.Tests/Support/Dispatcher/DispatcherTests.cs +++ b/src/CatLib.Core.Tests/Support/Dispatcher/DispatcherTests.cs @@ -37,7 +37,7 @@ public void TestSimpleOnEvents() var dispatcher = app.Make(); var isCall = false; - dispatcher.On("event.name", (payload) => + dispatcher.On("event.name", (object payload) => { isCall = true; Assert.AreEqual(123, payload); @@ -59,13 +59,13 @@ public void TestTriggerReturnResult() var dispatcher = app.Make(); var isCall = false; - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.name", (object payload) => { isCall = true; Assert.AreEqual(123, payload); return 1; }); - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.name", (object payload) => { Assert.AreEqual(123, payload); return 2; @@ -85,23 +85,23 @@ public void TestAsteriskWildcard() var dispatcher = app.Make(); var isCall = false; - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.name", (object payload) => { isCall = true; Assert.AreEqual(123, payload); return 1; }); - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.name", ( object payload) => { Assert.AreEqual(123, payload); return 2; }); - dispatcher.Listen("event.age", (payload) => + dispatcher.Listen("event.age", (object payload) => { Assert.AreEqual(123, payload); return 2; }); - dispatcher.Listen("event.*", (payload) => + dispatcher.Listen("event.*", (string eventName, object payload) => { Assert.AreEqual(123, payload); return 3; @@ -125,19 +125,19 @@ public void TestHalfTrigger() var dispatcher = app.Make(); var isCall = false; - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.name", (object payload) => { isCall = true; Assert.AreEqual(123, payload); return 1; }); - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.name", (object payload) => { isCall = true; Assert.AreEqual(123, payload); return 2; }); - dispatcher.Listen("event.*", (payload) => + dispatcher.Listen("event.*", (string eventName, object payload) => { Assert.AreEqual(123, payload); return 3; @@ -154,208 +154,292 @@ public void TestCancelHandler() var dispatcher = app.Make(); var isCall = false; - var handler = dispatcher.Listen("event.name", (payload) => + var handler = dispatcher.Listen("event.name", (object payload) => { isCall = true; Assert.AreEqual(123, payload); return 1; }); - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.name", (object payload) => { Assert.AreEqual(123, payload); return 2; }); - dispatcher.Listen("event.*", (payload) => + dispatcher.Listen("event.*", (string eventName, object payload) => { Assert.AreEqual(123, payload); return 3; }); - handler.Off(); + App.Off(handler); Assert.AreEqual(2, dispatcher.TriggerHalt("event.name", 123)); Assert.AreEqual(false, isCall); } [TestMethod] - public void TestLifeCall() + public void TestStopBubbling() { var app = MakeEnv(); var dispatcher = app.Make(); - var isCall = false; - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.*", (string eventName, object payload) => { - isCall = true; Assert.AreEqual(123, payload); return 1; - }, 1); + }); + dispatcher.Listen("event.time", (object payload) => + { + Assert.AreEqual(123, payload); + return 2; + }); + dispatcher.Listen("event.time", (object payload) => + { + Assert.AreEqual(123, payload); + return 3; + }); - Assert.AreEqual(1, dispatcher.TriggerHalt("event.name", 123)); - Assert.AreEqual(null, dispatcher.TriggerHalt("event.name", 123)); - Assert.AreEqual(true, isCall); + var results = dispatcher.Trigger("event.time", 123); + + Assert.AreEqual(3, results.Length); + Assert.AreEqual(2, results[0]); + Assert.AreEqual(3, results[1]); + Assert.AreEqual(1, results[2]); } [TestMethod] - public void TestDepthSelfTrigger() + public void TestHaltNull() { var app = MakeEnv(); var dispatcher = app.Make(); - var callNum = 0; - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.*", (string eventName, object payload) => { - dispatcher.Trigger("event.name", payload); - callNum++; Assert.AreEqual(123, payload); return 1; - }, 3); + }); - Assert.AreEqual(1, (dispatcher.Trigger("event.name", 123) as object[]).Length); - Assert.AreEqual(3, callNum); + dispatcher.Listen("event.time", (object payload) => + { + Assert.AreEqual(123, payload); + return null; + }); - Assert.AreEqual(null, dispatcher.TriggerHalt("event.name", 123)); + Assert.AreEqual(1, dispatcher.TriggerHalt("event.time", 123)); } [TestMethod] - public void TestOrderCancel() + public void TestBreakEvent() { var app = MakeEnv(); var dispatcher = app.Make(); - dispatcher.Listen("event.name", (payload) => + dispatcher.Listen("event.time", (object payload) => { Assert.AreEqual(123, payload); return 1; - }, 1); - dispatcher.Listen("event.name", (payload) => + }); + + dispatcher.Listen("event.time", (object payload) => { Assert.AreEqual(123, payload); - return 2; - }, 1); - dispatcher.Listen("event.*", (payload) => + return false; + }); + + dispatcher.Listen("event.time", (object payload) => { Assert.AreEqual(123, payload); - return 3; - }, 1); + return 2; + }); - Assert.AreEqual(1, dispatcher.TriggerHalt("event.name", 123)); - Assert.AreEqual(2, dispatcher.TriggerHalt("event.name", 123)); - Assert.AreEqual(3, dispatcher.TriggerHalt("event.name", 123)); - Assert.AreEqual(null, dispatcher.TriggerHalt("event.name", 123)); + var result = dispatcher.Trigger("event.time", 123); + Assert.AreEqual(1, result.Length); + Assert.AreEqual(1, result[0]); } [TestMethod] - public void TestRepeatOn() + public void TestAllHaltNull() { var app = MakeEnv(); var dispatcher = app.Make(); - dispatcher.Listen("event.*", (payload) => + dispatcher.Listen("event.time", (object payload) => { - Assert.AreEqual(123, payload); - return 1; - }, 1); + return null; + }); - dispatcher.Listen("event.*", (payload) => + dispatcher.Listen("event.time", (object payload) => { - Assert.AreEqual(123, payload); - return 2; - }, 1); + return null; + }); - Assert.AreEqual(1, dispatcher.TriggerHalt("event.name", 123)); - Assert.AreEqual(2, dispatcher.TriggerHalt("event.name", 123)); - Assert.AreEqual(null, dispatcher.TriggerHalt("event.name", 123)); + Assert.AreEqual(null, dispatcher.TriggerHalt("event.time")); } [TestMethod] - public void TestOtherWildcardOn() + public void TestOffWithEventName() { var app = MakeEnv(); - var dispatcher = app.Make(); - dispatcher.Listen("event.*", (payload) => + var handler = dispatcher.Listen("event.time", (object payload) => { - Assert.AreEqual(123, payload); return 1; - }, 1); + }); - dispatcher.Listen("event.call.*", (payload) => + dispatcher.Listen("event.time", (object payload) => { - Assert.AreEqual(123, payload); - return 2; - }, 1); + return 1; + }); - dispatcher.Listen("event2.call.*", (payload) => + var handler2 = dispatcher.Listen("event.*", (object payload) => { - Assert.AreEqual(123, payload); - return 3; - }, 1); + return 1; + }); + + App.Off("event.time"); + App.Off("event.time"); // double remove + App.Off("event.*"); + App.Off("event.*.Empty"); + App.Off(handler); + App.Off(handler2); + + Assert.AreEqual(0, dispatcher.Trigger("event.time").Length); + } + + public class TestEventClass + { + public object TestFuncHello() + { + return "hello"; + } - Assert.AreEqual(1, dispatcher.TriggerHalt("event.call.name", 123)); - Assert.AreEqual(2, dispatcher.TriggerHalt("event.call.name", 123)); - Assert.AreEqual(null, dispatcher.TriggerHalt("event.call.name", 123)); + public object TestFuncWorld(IContainer container) + { + Assert.AreNotEqual(null, container); + return "world"; + } } [TestMethod] - public void TestStopBubbling() + public void TestOffWithObject() { var app = MakeEnv(); + var dispatcher = app.Make(); + + var cls = new TestEventClass(); + dispatcher.On("MyTestEventClass.TestFuncHello", cls); + dispatcher.On("MyTestEventClass.*", cls, "TestFuncWorld"); + + Assert.AreEqual("hello", dispatcher.TriggerHalt("MyTestEventClass.TestFuncHello")); + Assert.AreEqual("world", dispatcher.TriggerHalt("MyTestEventClass.Jump")); + App.Off(cls); + App.Off(cls); // double off + + Assert.AreEqual(null, dispatcher.TriggerHalt("MyTestEventClass.TestFuncHello")); + Assert.AreEqual(null, dispatcher.TriggerHalt("MyTestEventClass.Jump")); + } + + [TestMethod] + public void TestHasListeners() + { + var app = MakeEnv(); var dispatcher = app.Make(); - dispatcher.Listen("event.*", (payload) => + + var cls = new TestEventClass(); + dispatcher.On("MyTestEventClass.TestFuncHello", cls); + dispatcher.On("MyTestEventClass.*", cls, "TestFuncWorld"); + var isCall = false; + dispatcher.On("MyTestEventClass.*", (string name) => { - Assert.AreEqual(123, payload); - return 1; + isCall = true; + Assert.AreEqual("MyTestEventClass.Jump.Hack", name); }); - dispatcher.Listen("event.time", (payload) => + + Assert.AreEqual(false, App.HasListeners("Null")); + Assert.AreEqual(true, App.HasListeners("MyTestEventClass.TestFuncHello")); + Assert.AreEqual(true, App.HasListeners("MyTestEventClass.Jump.Hack")); + Assert.AreEqual(false, App.HasListeners("MyTestEventClass.Jump.Hack", true)); + App.Trigger("MyTestEventClass.Jump.Hack"); + Assert.AreEqual(true, isCall); + } + + [TestMethod] + public void TestActionFuncCover() + { + var app = MakeEnv(); + var dispatcher = app.Make(); + + App.On("ActionTest", () => { - Assert.AreEqual(123, payload); - return 2; }); - dispatcher.Listen("event.time", (payload) => - { - Assert.AreEqual(123, payload); - return false; - }, 1); - dispatcher.Listen("event.time", (payload) => + + App.On("ActionTest", (IContainer container) => { - Assert.AreEqual(123, payload); - return 4; + Assert.AreNotEqual(null, container); }); - var results = dispatcher.Trigger("event.time", 123); + App.On("ActionTest", (IContainer container, object data) => + { + Assert.AreEqual(typeof(string), data.GetType()); + Assert.AreNotEqual(null, container); + }); - Assert.AreEqual(1, results.Length); - Assert.AreEqual(2, results[0]); + App.On("ActionTest2", (int num, int num2 ,int num3) => + { + Assert.AreEqual(1, num); + Assert.AreEqual(2, num2); + Assert.AreEqual(3, num3); + }); - results = dispatcher.Trigger("event.time", 123); + App.On("ActionTest2", (int num, int num2, int num3, int num4) => + { + Assert.AreEqual(1, num); + Assert.AreEqual(2, num2); + Assert.AreEqual(3, num3); + Assert.AreEqual(4, num4); + }); - Assert.AreEqual(3, results.Length); - Assert.AreEqual(2, results[0]); - Assert.AreEqual(4, results[1]); - Assert.AreEqual(1, results[2]); - } + App.Trigger("ActionTest", "helloworld"); + App.Trigger("ActionTest2", 1, 2, 3, 4); - [TestMethod] - public void TestHaltNull() - { - var app = MakeEnv(); + App.Listen("FuncTest", () => + { + return 0; + }); - var dispatcher = app.Make(); - dispatcher.Listen("event.*", (payload) => + App.Listen("FuncTest", (IContainer container) => { - Assert.AreEqual(123, payload); + Assert.AreNotEqual(null, container); return 1; }); - dispatcher.Listen("event.time", (payload) => + App.Listen("FuncTest", (IContainer container, object data) => { - Assert.AreEqual(123, payload); - return null; + Assert.AreEqual(typeof(string), data.GetType()); + Assert.AreNotEqual(null, container); + return 2; }); - Assert.AreEqual(1, dispatcher.TriggerHalt("event.time", 123)); + App.Listen("FuncTest2", (int num, int num2, int num3) => + { + Assert.AreEqual(1, num); + Assert.AreEqual(2, num2); + Assert.AreEqual(3, num3); + return 3; + }); + + App.Listen("FuncTest2", (int num, int num2, int num3, int num4) => + { + Assert.AreEqual(1, num); + Assert.AreEqual(2, num2); + Assert.AreEqual(3, num3); + Assert.AreEqual(4, num4); + return 4; + }); + + Assert.AreEqual(3, App.Trigger("FuncTest", "helloworld").Length); + Assert.AreEqual(2, App.Trigger("FuncTest2", 1, 2, 3, 4).Length); } } } diff --git a/src/CatLib.Core.Tests/Support/LruCache/LruCacheTests.cs b/src/CatLib.Core.Tests/Support/LruCache/LruCacheTests.cs deleted file mode 100644 index 4969984..0000000 --- a/src/CatLib.Core.Tests/Support/LruCache/LruCacheTests.cs +++ /dev/null @@ -1,230 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace CatLib.Tests.Stl -{ - [TestClass] - public class LruCacheTests - { - /// - /// 增加测试 - /// - [TestMethod] - public void AddTest() - { - var cache = new LruCache(5); - for (var i = 0; i < 5; i++) - { - cache.Add(i.ToString(), i.ToString()); - } - - var n = 5; - foreach (var v in cache) - { - Assert.AreEqual((--n).ToString(), v.Value); - } - } - - /// - /// 获取测试 - /// - [TestMethod] - public void GetTest() - { - var cache = new LruCache(5); - for (var i = 0; i < 5; i++) - { - cache.Add(i.ToString(), i.ToString()); - } - - var result = cache["0"]; - result = cache["1"]; - - if (result == null) - { - Assert.Fail(); - } - - foreach (var v in new[] { "1", "0", "4", "3", "2" }) - { - Assert.AreEqual(v, cache[v]); - } - } - - /// - /// 覆盖测试 - /// - [TestMethod] - public void ReplaceTest() - { - var cache = new LruCache(5); - for (var i = 0; i < 5; i++) - { - cache.Add(i.ToString(), i.ToString()); - } - - cache["0"] = "10"; - cache["1"] = "11"; - - foreach (var v in new[] { "1", "0", "4", "3", "2" }) - { - if (v == "0") - { - Assert.AreEqual("10", cache[v]); - } - else if (v == "1") - { - Assert.AreEqual("11", cache[v]); - } - else - { - Assert.AreEqual(v, cache[v]); - } - } - } - - /// - /// 测试移除事件 - /// - [TestMethod] - public void TestRemoveEvent() - { - var cache = new LruCache(5); - for (var i = 0; i < 5; i++) - { - cache.Add(i.ToString(), i.ToString()); - } - - var callNum = 0; - cache.OnRemoveLeastUsed += (key, val) => - { - if (callNum++ <= 0) - { - cache.Get(key); - } - }; - cache.Add("10", "10"); - - Assert.AreEqual(default(string), cache.Get("1")); - Assert.AreEqual(5, cache.Count); - Assert.AreEqual(1, callNum); - } - - /// - /// 末尾移除测试 - /// - [TestMethod] - public void RemoveLastedTest() - { - var cache = new LruCache(5); - for (var i = 0; i < 5; i++) - { - cache.Add(i.ToString(), i.ToString()); - } - cache["0"] = "0"; - cache["1"] = "1"; - cache.Add("999", "999"); - - Assert.AreEqual(5, cache.Count); - foreach (var v in new[] { "999", "1", "0", "4", "3" }) - { - Assert.AreEqual(v, cache[v]); - } - } - - /// - /// 获取不存在元素的测试 - /// - [TestMethod] - public void GetNotExistsKey() - { - var cache = new LruCache(5); - Assert.AreEqual(null, cache["123"]); - Assert.AreEqual("notExists", cache.Get("111", "notExists")); - } - - /// - /// 移除测试 - /// - [TestMethod] - public void RemoveTest() - { - var cache = new LruCache(5); - for (var i = 0; i < 5; i++) - { - cache.Add(i.ToString(), i.ToString()); - } - - foreach (var v in new[] { "2", "1", "3" }) - { - cache.Remove(v); - } - - foreach (var v in new[] { "4", "0" }) - { - Assert.AreEqual(v, cache[v]); - } - - Assert.AreEqual(2, cache.Count); - } - - /// - /// 在头部和尾部移除 - /// - [TestMethod] - public void RemoveWithHeaderAndTail() - { - var cache = new LruCache(5); - for (var i = 0; i < 5; i++) - { - cache.Add(i.ToString(), i.ToString()); - } - - foreach (var v in new[] { "0", "2", "4" }) - { - cache.Remove(v); - } - - foreach (var v in new[] { "3", "1" }) - { - Assert.AreEqual(v, cache[v]); - } - - Assert.AreEqual(2, cache.Count); - } - - /// - /// 移除不存在的元素 - /// - [TestMethod] - public void RemoveNotExists() - { - var cache = new LruCache(5); - cache.Remove("999"); - } - - [TestMethod] - public void TestGet() - { - var cache = new LruCache(5); - cache.Add("10", "5"); - - string val; - Assert.AreEqual(true, cache.Get("10", out val, "100")); - Assert.AreEqual("5", val); - - Assert.AreEqual(false, cache.Get("11", out val, "100")); - Assert.AreEqual("100", val); - } - } -} diff --git a/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs b/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs index ee13d43..1472968 100644 --- a/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs +++ b/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs @@ -21,7 +21,7 @@ private class TestManagerClass : Manager { public Func GetResolvePublic(string name) { - return GetResolve(name); + return GetExtend(name); } } diff --git a/src/CatLib.Core.Tests/Support/Util/ArrTests.cs b/src/CatLib.Core.Tests/Support/Util/ArrTests.cs index 2c65e5e..35eb714 100644 --- a/src/CatLib.Core.Tests/Support/Util/ArrTests.cs +++ b/src/CatLib.Core.Tests/Support/Util/ArrTests.cs @@ -456,6 +456,12 @@ public void TestReverse() Assert.AreEqual("a", data[0]); Assert.AreEqual("b", data[1]); Assert.AreEqual("c", data[2]); + + data = new[] {"a"}; + result = Arr.Reverse(data); + + Assert.AreEqual(1, result.Length); + Assert.AreEqual("a", data[0]); } [TestMethod] @@ -520,6 +526,112 @@ public void TestDifference() Assert.AreEqual("c", result[2]); } + [TestMethod] + public void TestDifferenceEmptyMatch() + { + var data = new[] { "a", "b", "c", "d", "e" }; + var result = Arr.Difference(data, null); + Assert.AreEqual(5, result.Length); + Assert.AreEqual("a", result[0]); + Assert.AreEqual("b", result[1]); + Assert.AreEqual("c", result[2]); + Assert.AreEqual("d", result[3]); + Assert.AreEqual("e", result[4]); + } + [TestMethod] + public void TestRemoveAt() + { + var data = new[] { "a", "b", "c", "d", "e" }; + var result = Arr.RemoveAt(ref data, 1); + Assert.AreEqual("b", result); + Assert.AreEqual("a", data[0]); + Assert.AreEqual("c", data[1]); + Assert.AreEqual("d", data[2]); + Assert.AreEqual("e", data[3]); + Assert.AreEqual(4, data.Length); + + result = Arr.RemoveAt(ref data, 3); + Assert.AreEqual("e", result); + Assert.AreEqual("a", data[0]); + Assert.AreEqual("c", data[1]); + Assert.AreEqual("d", data[2]); + Assert.AreEqual(3, data.Length); + + result = Arr.RemoveAt(ref data, 0); + Assert.AreEqual("a", result); + Assert.AreEqual("c", data[0]); + Assert.AreEqual("d", data[1]); + Assert.AreEqual(2, data.Length); + + bool ex = false; + try + { + result = Arr.RemoveAt(ref data, 2); + } + catch (Exception) + { + ex = true; + } + Assert.AreEqual(true, ex); + + result = Arr.RemoveAt(ref data, 0); + Assert.AreEqual("c", result); + Assert.AreEqual("d", data[0]); + Assert.AreEqual(1, data.Length); + } + + [TestMethod] + public void TestRemoveAtNegativeNumber() + { + var data = new[] { "a", "b", "c", "d", "e" }; + var result = Arr.RemoveAt(ref data, -2); + Assert.AreEqual("d", result); + Assert.AreEqual("a", data[0]); + Assert.AreEqual("b", data[1]); + Assert.AreEqual("c", data[2]); + Assert.AreEqual("e", data[3]); + Assert.AreEqual(4, data.Length); + + result = Arr.RemoveAt(ref data, -1); + Assert.AreEqual("e", result); + Assert.AreEqual("a", data[0]); + Assert.AreEqual("b", data[1]); + Assert.AreEqual("c", data[2]); + Assert.AreEqual(3, data.Length); + + result = Arr.RemoveAt(ref data, -999); + Assert.AreEqual("a", result); + Assert.AreEqual("b", data[0]); + Assert.AreEqual("c", data[1]); + Assert.AreEqual(2, data.Length); + + result = Arr.RemoveAt(ref data, -1); + Assert.AreEqual("c", result); + Assert.AreEqual("b", data[0]); + Assert.AreEqual(1, data.Length); + + result = Arr.RemoveAt(ref data, -1); + Assert.AreEqual("b", result); + Assert.AreEqual(0, data.Length); + + data = new string[] { }; + result = Arr.RemoveAt(ref data, -1); + Assert.AreEqual(null, result); + Assert.AreEqual(0, data.Length); + } + + [TestMethod] + public void TestEmptyFlash() + { + var data = new int[] { }; + var isCall = false; + Arr.Flash(data, (i) => { }, (o) => { }, () => + { + isCall = true; + }); + + Assert.AreEqual(true, isCall); + } } } diff --git a/src/CatLib.Core.Tests/Support/Util/DictTests.cs b/src/CatLib.Core.Tests/Support/Util/DictTests.cs new file mode 100644 index 0000000..845377c --- /dev/null +++ b/src/CatLib.Core.Tests/Support/Util/DictTests.cs @@ -0,0 +1,221 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System.Collections.Generic; +using CatLib.Tests; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CatLib.Core.Tests.Support.Util +{ + [TestClass] + public class DictTests + { + [TestMethod] + public void TestGet() + { + var dict = new Dictionary + { + { "my" , new Dictionary + { + {"name" , new Dictionary + { + { "is" , "catlib" } + }}, + + {"age" , new Dictionary + { + { "is" , 18 } + }}, + } } + }; + + Assert.AreEqual("catlib", Dict.Get(dict, "my.name.is")); + Assert.AreEqual(18, Dict.Get(dict, "my.age.is")); + Assert.AreEqual("undefiend", Dict.Get(dict, "my.age.undefiend","undefiend")); + + Assert.AreEqual("undefiend", Dict.Get(null, "my.age.undefiend", "undefiend")); + Assert.AreEqual(dict, Dict.Get(dict, null, "undefiend")); + + Assert.AreEqual("undefiend", Dict.Get(dict, "my.age.is.name", "undefiend")); + } + + [TestMethod] + public void TestSetRemove() + { + var dict = new Dictionary(); + + Dict.Set(dict, "hello.world", "hello"); + Dict.Set(dict, "hello.name", "catlib"); + Dict.Set(dict, "hello.world.name", "c#"); + Dict.Set(dict, "hello.world.just", "j#"); + + Assert.AreEqual(((Dictionary)dict["hello"])["world"], Dict.Get(dict, "hello.world")); + Assert.AreEqual("c#", Dict.Get(dict, "hello.world.name")); + Assert.AreEqual("j#", Dict.Get(dict, "hello.world.just")); + Assert.AreEqual("catlib", Dict.Get(dict, "hello.name")); + + Dict.Remove(dict, "hello.world.name"); + Dict.Remove(dict, "hello.world.just"); + + Assert.AreEqual("undefiend", Dict.Get(dict, "hello.world", "undefiend")); + Assert.AreEqual("catlib", Dict.Get(dict, "hello.name", "undefiend")); + + dict = new Dictionary(); + Dict.Set(dict, "hello.world.name.is", "hello"); + Dict.Remove(dict, "hello.world.name.is"); + Assert.AreEqual("undefiend", Dict.Get(dict, "hello", "undefiend")); + + Assert.AreEqual(false, Dict.Remove(dict, "notexists.notexists")); + Assert.AreEqual(false, Dict.Remove(dict, "hello.name.is.world")); + } + + [TestMethod] + public void TestKeys() + { + var dict = new Dictionary + { + {"1", 1}, + {"2", 1}, + {"3", 1} + }; + + var arr = Dict.Keys(dict); + + Assert.AreEqual("1", arr[0]); + Assert.AreEqual("2", arr[1]); + Assert.AreEqual("3", arr[2]); + } + + [TestMethod] + public void TestValues() + { + var dict = new Dictionary + { + {"1", 1}, + {"2", 2}, + {"3", 3} + }; + + var arr = Dict.Values(dict); + + Assert.AreEqual(1, arr[0]); + Assert.AreEqual(2, arr[1]); + Assert.AreEqual(3, arr[2]); + } + + [TestMethod] + public void TestMap() + { + var dict = new Dictionary + { + {"1", 1}, + {"2", 2}, + {"3", 3} + }; + + var arr = Dict.Values(Dict.Map(dict, (key, val) => val * 2)); + + Assert.AreEqual(2, arr[0]); + Assert.AreEqual(4, arr[1]); + Assert.AreEqual(6, arr[2]); + } + + [TestMethod] + public void TestModify() + { + var dict = new Dictionary + { + {"1", 1}, + {"2", 2}, + {"3", 3} + }; + + Dict.Modify(dict, (k, v) => v * 2); + + var arr = Dict.Values(dict); + + Assert.AreEqual(2, arr[0]); + Assert.AreEqual(4, arr[1]); + Assert.AreEqual(6, arr[2]); + } + + [TestMethod] + public void TestRemove() + { + var dict = new Dictionary + { + {"1", 1}, + {"2", 2}, + {"3", 3} + }; + + Dict.Remove(dict, (k, v) => v == 2); + var arr = Dict.Values(dict); + + Assert.AreEqual(1, arr[0]); + Assert.AreEqual(3, arr[1]); + } + + [TestMethod] + public void TestFilter() + { + var dict = new Dictionary + { + {"1", 1}, + {"2", 2}, + {"3", 3} + }; + + var arr = Dict.Values(Dict.Filter(dict, (k, v) => v == 2)); + Assert.AreEqual(2, arr[0]); + } + + [TestMethod] + public void TestAddRang() + { + var dict = new Dictionary + { + {"1", 1}, + {"2", 2}, + {"3", 3} + }; + + Dict.AddRange(dict, new Dictionary + { + { "9", 9 }, + { "10", 10 } + }); + + ExceptionAssert.Throws(() => + { + Dict.AddRange(dict, new Dictionary + { + {"9", 9}, + {"10", 10} + }, false); + }); + + Dict.AddRange(dict, new Dictionary + { + {"10", 12} + }); + + ExceptionAssert.DoesNotThrow(() => + { + Dict.AddRange(dict, null); + }); + + Assert.AreEqual(true, dict.ContainsKey("9")); + Assert.AreEqual(true, dict.ContainsKey("10")); + Assert.AreEqual(12, dict["10"]); + } + } +} diff --git a/src/CatLib.Core.Tests/Support/Util/StrTests.cs b/src/CatLib.Core.Tests/Support/Util/StrTests.cs index a948b75..1b75ae8 100644 --- a/src/CatLib.Core.Tests/Support/Util/StrTests.cs +++ b/src/CatLib.Core.Tests/Support/Util/StrTests.cs @@ -308,5 +308,20 @@ public void TestTruncate() str = Str.Truncate("喵h喵e越l来l越l漂o亮!了", 12); Assert.AreEqual("喵h喵e越l来l越...", str); } + + [TestMethod] + public void TestStrMethod() + { + Assert.AreEqual("GetNameSpace", Str.Method("Helloworld.GetNameSpace")); + Assert.AreEqual("GetNameSpace", Str.Method("Helloworld.GetNameSpace()")); + Assert.AreEqual("Space", Str.Method("Helloworld.GetName@Space()")); + Assert.AreEqual("_Space", Str.Method("Helloworld.GetName@_Space()")); + Assert.AreEqual("g8975GetNameSpace", Str.Method("Helloworld.g8975GetNameSpace()")); + Assert.AreEqual("GetNameSpace", Str.Method("Helloworld.8975GetNameSpace()")); + Assert.AreEqual("ame_Space", Str.Method("Helloworld.8975GetN(;)ame_Space()")); + Assert.AreEqual("GetName_Space", Str.Method("Helloworld.8GetName_Space()")); + Assert.AreEqual(string.Empty, Str.Method(null)); + Assert.AreEqual(string.Empty, Str.Method(string.Empty)); + } } } diff --git a/src/CatLib.Core.Tests/Support/VersionTests.cs b/src/CatLib.Core.Tests/Support/VersionTests.cs index 883b4b7..e7bc5b6 100644 --- a/src/CatLib.Core.Tests/Support/VersionTests.cs +++ b/src/CatLib.Core.Tests/Support/VersionTests.cs @@ -109,31 +109,38 @@ public void ThrowErrorVersion() { ExceptionAssert.Throws(() => { - new Version("1.01.2-beta.10+29830"); + var v = new Version("1.01.2-beta.10+29830"); + v.Compare("1.0.0"); }); ExceptionAssert.Throws(() => { - new Version("1.1b.2-beta.10+29830"); + var v = new Version("1.1b.2-beta.10+29830"); + v.Compare("1.0.0"); }); ExceptionAssert.Throws(() => { - new Version("1.1.2/beta.10+29830"); + var v = new Version("1.1.2/beta.10+29830"); + v.Compare("1.0.0"); }); ExceptionAssert.Throws(() => { - new Version("1.1.2-00.10+29830"); + var v = new Version("1.1.2-00.10+29830"); + v.Compare("1.0.0"); }); ExceptionAssert.Throws(() => { - new Version("1.1.2-+29830"); + var v = new Version("1.1.2-+29830"); + v.Compare("1.0.0"); }); ExceptionAssert.Throws(() => { - new Version("1.1.2-0.+29830"); + var v = new Version("1.1.2-0.+29830"); + v.Compare("1.0.0"); }); ExceptionAssert.Throws(() => { - new Version("1.1..2-0.beta2+29830"); + var v = new Version("1.1..2-0.beta2+29830"); + v.Compare("1.0.0"); }); } } diff --git a/src/CatLib.Core/CatLib.Core.csproj b/src/CatLib.Core/CatLib.Core.csproj index 86e3d2c..1b15f77 100644 --- a/src/CatLib.Core/CatLib.Core.csproj +++ b/src/CatLib.Core/CatLib.Core.csproj @@ -45,10 +45,17 @@ - - - - + + + + + + + + + + + @@ -58,19 +65,19 @@ - - + + + + - - - + diff --git a/src/CatLib.Core/CatLib/App.cs b/src/CatLib.Core/CatLib/App.cs index f3371e5..5f0b7f3 100644 --- a/src/CatLib.Core/CatLib/App.cs +++ b/src/CatLib.Core/CatLib/App.cs @@ -10,6 +10,7 @@ */ using System; +using System.Collections.Generic; using System.Reflection; namespace CatLib @@ -18,8 +19,9 @@ namespace CatLib /// CatLib实例 /// [ExcludeFromCodeCoverage] - public sealed class App + public class App { + #region Original /// /// 当新建Application时 /// @@ -52,7 +54,9 @@ public static IApplication Handler } } } + #endregion + #region Application API /// /// 注册服务提供者 /// @@ -144,60 +148,190 @@ public static int GetPriority(Type type, string method = null) } /// - /// 设定调试等级 + /// 调试等级 /// - /// 调试等级 - public static void SetDebugLevel(DebugLevels level) + public static DebugLevels DebugLevel { - Handler.SetDebugLevel(level); + get { return Handler.DebugLevel; } + set { Handler.DebugLevel = value; } + } + #endregion + + #region Dispatcher API + /// + /// 判断给定事件是否存在事件监听器 + /// + /// 事件名 + /// + /// 严格模式 + /// 启用严格模式则不使用正则来进行匹配事件监听器 + /// + /// 是否存在事件监听器 + public static bool HasListeners(string eventName, bool strict = false) + { + return Handler.HasListeners(eventName, strict); } /// /// 触发一个事件,并获取事件的返回结果 /// /// 事件名称 - /// 载荷 + /// 载荷 /// 事件结果 - public static object Trigger(string eventName, object payload = null) + public static object[] Trigger(string eventName, params object[] payloads) { - return Handler.Trigger(eventName, payload); + return Handler.Trigger(eventName, payloads); } /// /// 触发一个事件,遇到第一个事件存在处理结果后终止,并获取事件的返回结果 /// /// 事件名 - /// 载荷 + /// 载荷 /// 事件结果 - public static object TriggerHalt(string eventName, object payload = null) + public static object TriggerHalt(string eventName, params object[] payloads) + { + return Handler.TriggerHalt(eventName, payloads); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(string eventName, Action method) + { + return Handler.On(eventName, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件调用目标 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(string eventName, object target, string method = null) + { + return Handler.On(eventName, target, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(string eventName, Action method) + { + return Handler.On(eventName, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(string eventName, Action method) + { + return Handler.On(eventName, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(string eventName, Action method) + { + return Handler.On(eventName, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(string eventName, Action method) + { + return Handler.On(eventName, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(string eventName, Func method) + { + return Handler.Listen(eventName, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(string eventName, Func method) + { + return Handler.Listen(eventName, method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(string eventName, Func method) { - return Handler.TriggerHalt(eventName, payload); + return Handler.Listen(eventName, method); } /// - /// 注册一个事件 + /// 注册一个事件监听器 /// /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - public static IEventHandler On(string eventName, Action handler, int life = 0) + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(string eventName, Func method) { - return Handler.On(eventName, handler, life); + return Handler.Listen(eventName, method); } /// - /// 注册一个事件 + /// 注册一个事件监听器 /// /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - public static IEventHandler Listen(string eventName, Func handler, int life = 0) + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(string eventName, Func method) + { + return Handler.Listen(eventName, method); + } + + /// + /// 解除注册的事件监听器 + /// + /// + /// 事件解除目标 + /// 如果传入的是字符串(string)将会解除对应事件名的所有事件 + /// 如果传入的是事件对象(IEvent)那么解除对应事件 + /// 如果传入的是其他实例(object)会解除该实例下的所有事件 + /// + public static void Off(object target) { - return Handler.Listen(eventName, handler, life); + Handler.Off(target); } + #endregion + #region Container API /// /// 获取服务的绑定数据,如果绑定不存在则返回null /// @@ -218,6 +352,36 @@ public static bool HasBind(string service) return Handler.HasBind(service); } + /// + /// 是否已经实例静态化 + /// + /// 服务名 + /// 是否已经静态化 + public static bool HasInstance() + { + return Handler.HasInstance(); + } + + /// + /// 服务是否已经被解决过 + /// + /// 服务名 + /// 是否已经被解决过 + public static bool IsResolved() + { + return Handler.IsResolved(); + } + + /// + /// 是否可以生成服务 + /// + /// 服务名或者别名 + /// 是否可以生成服务 + public static bool CanMake(string service) + { + return Handler.CanMake(service); + } + /// /// 服务是否是静态化的,如果服务不存在也将返回false /// @@ -228,14 +392,24 @@ public static bool IsStatic(string service) return Handler.IsStatic(service); } + /// + /// 是否是别名 + /// + /// 名字 + /// 是否是别名 + public static bool IsAlias(string name) + { + return Handler.IsAlias(name); + } + /// /// 绑定一个服务 /// /// 服务名 - /// 服务实体 + /// 服务实现 /// 服务是否静态化 /// 服务绑定数据 - public static IBindData Bind(string service, Func concrete, bool isStatic) + public static IBindData Bind(string service, Type concrete, bool isStatic) { return Handler.Bind(service, concrete, isStatic); } @@ -244,10 +418,10 @@ public static IBindData Bind(string service, Func /// 绑定一个服务 /// /// 服务名 - /// 服务实现 + /// 服务实体 /// 服务是否静态化 /// 服务绑定数据 - public static IBindData Bind(string service, Type concrete, bool isStatic) + public static IBindData Bind(string service, Func concrete, bool isStatic) { return Handler.Bind(service, concrete, isStatic); } @@ -258,10 +432,11 @@ public static IBindData Bind(string service, Type concrete, bool isStatic) /// 服务名 /// 服务实现 /// 服务是否是静态的 + /// 如果绑定失败则返回历史绑定对象 /// 服务绑定数据 - public static IBindData BindIf(string service, Func concrete, bool isStatic) + public static bool BindIf(string service, Func concrete, bool isStatic, out IBindData bindData) { - return Handler.BindIf(service, concrete, isStatic); + return Handler.BindIf(service, concrete, isStatic, out bindData); } /// @@ -270,10 +445,46 @@ public static IBindData BindIf(string service, Func服务名 /// 服务实现 /// 服务是否是静态的 + /// 如果绑定失败则返回历史绑定对象 /// 服务绑定数据 - public static IBindData BindIf(string service, Type concrete, bool isStatic) + public static bool BindIf(string service, Type concrete, bool isStatic, out IBindData bindData) + { + return Handler.BindIf(service, concrete, isStatic, out bindData); + } + + /// + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用目标 + /// 调用方法 + /// 方法绑定数据 + public static IMethodBind BindMethod(string method, object target, MethodInfo call) + { + return Handler.BindMethod(method, target, call); + } + + /// + /// 解除绑定的方法 + /// + /// + /// 解除目标 + /// 如果为字符串则作为调用方法名 + /// 如果为IMethodBind则作为指定方法 + /// 如果为其他对象则作为调用目标做全体解除 + /// + public static void UnbindMethod(object target) { - return Handler.BindIf(service, concrete, isStatic); + Handler.UnbindMethod(target); + } + + /// + /// 解除绑定服务 + /// + /// 服务名或者别名 + public static void Unbind(string service) + { + Handler.Unbind(service); } /// @@ -301,9 +512,9 @@ public static object[] Tagged(string tag) /// /// 服务名或者别名 /// 服务实例 - public static void Instance(string service, object instance) + public static object Instance(string service, object instance) { - Handler.Instance(service, instance); + return Handler.Instance(service, instance); } /// @@ -316,24 +527,14 @@ public static void Release(string service) } /// - /// 当静态服务被释放时 + /// 调用一个已经被绑定的方法 /// - /// 处理释放时的回调 - public static IContainer OnRelease(Action action) - { - return Handler.OnRelease(action); - } - - /// - /// 以依赖注入形式调用一个方法 - /// - /// 方法对象 /// 方法名 - /// 方法参数 - /// 方法返回值 - public static object Call(object instance, string method, params object[] param) + /// 用户提供的参数 + /// 调用结果 + public static object Invoke(string method, params object[] userParams) { - return Handler.Call(instance, method, param); + return Handler.Invoke(method, userParams); } /// @@ -341,32 +542,32 @@ public static object Call(object instance, string method, params object[] param) /// /// 方法对象 /// 方法信息 - /// 方法参数 + /// 用户传入的参数 /// 方法返回值 - public static object Call(object instance, MethodInfo methodInfo, params object[] param) + public static object Call(object instance, MethodInfo methodInfo, params object[] userParams) { - return Handler.Call(instance, methodInfo, param); + return Handler.Call(instance, methodInfo, userParams); } /// /// 构造服务 /// /// 服务名或别名 - /// 构造参数 + /// 用户传入的参数 /// 服务实例,如果构造失败那么返回null - public static object MakeWith(string service, params object[] param) + public static object Make(string service, params object[] userParams) { - return Handler.MakeWith(service, param); + return Handler.Make(service, userParams); } /// - /// 构造服务 + /// 获取一个回调,当执行回调可以生成指定的服务 /// /// 服务名或别名 - /// 服务实例,如果构造失败那么返回null - public static object Make(string service) + /// 回调方案 + public static Func Factory(string service) { - return Handler.Make(service); + return Handler.Factory(service); } /// @@ -390,6 +591,15 @@ public static IContainer OnResolving(Func func) return Handler.OnResolving(func); } + /// + /// 当静态服务被释放时 + /// + /// 处理释放时的回调 + public static IContainer OnRelease(Action action) + { + return Handler.OnRelease(action); + } + /// /// 当查找类型无法找到时会尝试去调用开发者提供的查找类型函数 /// @@ -401,6 +611,40 @@ public static IContainer OnFindType(Func func, int priority = int. return Handler.OnFindType(func, priority); } + /// + /// 当一个已经被解决的服务,发生重定义时触发 + /// + /// 服务名 + /// 回调 + /// 服务容器 + public static IContainer OnRebound(string service, Action callback) + { + return Handler.OnRebound(service, callback); + } + + /// + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// 服务的新建(第一次解决服务)操作并不会触发重定义 + /// + /// 关注的服务名 + /// 当服务发生重定义时调用的目标 + /// 方法信息 + public static void Watch(string service, object target, MethodInfo methodInfo) + { + Handler.Watch(service, target, methodInfo); + } + + /// + /// 在回调区间内暂时性的静态化服务实例 + /// + /// 回调区间 + /// 服务映射 + public static void Flash(Action callback, params KeyValuePair[] services) + { + Handler.Flash(callback, services); + } + /// /// 类型转为服务名 /// @@ -410,16 +654,170 @@ public static string Type2Service(Type type) { return Handler.Type2Service(type); } + #endregion + #region Container Extend API /// - /// 以单例的形式绑定一个服务 + /// 获取服务的绑定数据,如果绑定不存在则返回null + /// + /// 服务名 + /// 服务绑定数据或者null + public static IBindData GetBind() + { + return Handler.GetBind(); + } + + /// + /// 是否已经绑定了服务 + /// + /// 服务名 + /// 代表服务是否被绑定 + public static bool HasBind() + { + return Handler.HasBind(); + } + + /// + /// 是否可以生成服务 + /// + /// 服务名 + /// 服务是否可以被构建 + public static bool CanMake() + { + return Handler.CanMake(); + } + + /// + /// 服务是否是静态化的,如果服务不存在也将返回false + /// + /// 服务名 + /// 服务是否是静态化的 + public static bool IsStatic() + { + return Handler.IsStatic(); + } + + /// + /// 是否是别名 + /// + /// 服务名 + /// 是否是别名 + public static bool IsAlias() + { + return Handler.IsAlias(); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名,同时也是服务实现 + /// 服务绑定数据 + public static IBindData Bind() + { + return Handler.Bind(); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名 + /// 服务别名 + /// 服务绑定数据 + public static IBindData Bind() + { + return Handler.Bind(); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名 + /// 服务实现 + /// 服务绑定数据 + public static IBindData Bind(Func concrete) + { + return Handler.Bind(concrete); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名 + /// 服务实现 + /// 服务绑定数据 + public static IBindData Bind(Func concrete) + { + return Handler.Bind(concrete); + } + + /// + /// 常规绑定一个服务 /// /// 服务名 /// 服务实现 /// 服务绑定数据 - public static IBindData Singleton(string service, Func concrete) + public static IBindData Bind(string service, Func concrete) { - return Handler.Singleton(service, concrete); + return Handler.Bind(service, concrete); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务别名 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(out IBindData bindData) + { + return Handler.BindIf(out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名,同时也是服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(out IBindData bindData) + { + return Handler.BindIf(out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(Func concrete, out IBindData bindData) + { + return Handler.BindIf(concrete, out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(Func concrete, out IBindData bindData) + { + return Handler.BindIf(concrete, out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(string service, Func concrete, out IBindData bindData) + { + return Handler.BindIf(service, concrete, out bindData); } /// @@ -438,7 +836,7 @@ public static IBindData Singleton() /// /// 服务名,同时也是服务实现 /// 服务绑定数据 - public static IBindData Singleton() where TService : class + public static IBindData Singleton() { return Handler.Singleton(); } @@ -449,115 +847,372 @@ public static IBindData Singleton() where TService : class /// 服务名 /// 服务实现 /// 服务绑定数据 - public static IBindData Singleton(Func concrete) where TService : class + public static IBindData Singleton(Func concrete) { return Handler.Singleton(concrete); } /// - /// 常规绑定一个服务 + /// 以单例的形式绑定一个服务 /// /// 服务名 - /// 服务别名 + /// 服务实现 /// 服务绑定数据 - public static IBindData Bind() + public static IBindData Singleton(Func concrete) { - return Handler.Bind(); + return Handler.Singleton(concrete); } /// - /// 常规绑定一个服务 + /// 以单例的形式绑定一个服务 /// - /// 服务名,同时也是服务实现 + /// 服务名 + /// 服务实现 /// 服务绑定数据 - public static IBindData Bind() where TService : class + public static IBindData Singleton(string service, Func concrete) { - return Handler.Bind(); + return Handler.Singleton(service, concrete); } /// - /// 常规绑定一个服务 + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务别名 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(out IBindData bindData) + { + return Handler.SingletonIf(out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名,同时也是服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(out IBindData bindData) + { + return Handler.SingletonIf(out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 /// /// 服务名 /// 服务实现 - /// 服务绑定数据 - public static IBindData Bind(Func concrete) - where TService : class + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(Func concrete, out IBindData bindData) { - return Handler.Bind(concrete); + return Handler.SingletonIf(concrete, out bindData); } /// - /// 常规绑定一个服务 + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(Func concrete, out IBindData bindData) + { + return Handler.SingletonIf(concrete, out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 /// /// 服务名 /// 服务实现 - /// 服务绑定数据 - public static IBindData Bind(string service, - Func concrete) + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(string service, Func concrete, out IBindData bindData) { - return Handler.Bind(service, concrete); + return Handler.SingletonIf(service, concrete, out bindData); + } + + /// + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用目标 + /// 调用方法 + public static IMethodBind BindMethod(string method, object target, + string call = null) + { + return Handler.BindMethod(method, target, call); + } + + /// + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(string method, Func callback) + { + return Handler.BindMethod(method, callback); + } + + /// + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(string method, Func callback) + { + return Handler.BindMethod(method, callback); } /// - /// 构造一个服务,允许传入构造参数 + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(string method, Func callback) + { + return Handler.BindMethod(method, callback); + } + + /// + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(string method, Func callback) + { + return Handler.BindMethod(method, callback); + } + + /// + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(string method, Func callback) + { + return Handler.BindMethod(method, callback); + } + + /// + /// 解除服务绑定 + /// + /// 解除绑定的服务 + public static void Unbind() + { + Handler.Unbind(); + } + + /// + /// 静态化一个服务,实例值会经过解决修饰器 /// /// 服务名 - /// 构造参数 - /// 服务实例 - public static TService MakeWith(params object[] param) + /// 实例值 + public static void Instance(object instance) { - return Handler.MakeWith(param); + Handler.Instance(instance); + } + + /// + /// 释放服务 + /// + /// 服务名 + public static void Release() + { + Handler.Release(); + } + + /// + /// 以依赖注入形式调用一个方法 + /// + /// 方法对象 + /// 方法名 + /// 用户传入的参数 + /// 方法返回值 + public static object Call(object instance, string method, params object[] userParams) + { + return Handler.Call(instance, method, userParams); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 方法 + public static void Call(Action method) + { + Handler.Call(method); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 方法 + public static void Call(Action method) + { + Handler.Call(method); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 方法 + public static void Call(Action method) + { + Handler.Call(method); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 方法 + public static void Call(Action method) + { + Handler.Call(method); + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(Action method, params object[] userParams) + { + return Handler.Wrap(method, userParams); + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(Action method, params object[] userParams) + { + return Handler.Wrap(method, userParams); + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(Action method, params object[] userParams) + { + return Handler.Wrap(method, userParams); + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(Action method, params object[] userParams) + { + return Handler.Wrap(method, userParams); } /// /// 构造一个服务 /// /// 服务名 + /// 用户参数 /// 服务实例 - public static TService Make() + public static TService Make(params object[] userParams) { - return Handler.Make(); + return Handler.Make(userParams); } /// /// 构造一个服务 /// - /// 服务实例转换到的类型 - /// 服务名 + /// 服务类型 + /// 用户提供的参数 /// 服务实例 - public static TConvert Make(string service) + public static object Make(Type type, params object[] userParams) { - return Handler.Make(service); + return Handler.Make(type, userParams); } /// - /// 释放服务 + /// 获取一个回调,当执行回调可以生成指定的服务 /// /// 服务名 - [Obsolete("This function is about to be removed , Please use Release()", true)] - public static void Releases() where TService : class + /// 回调方案 + public static Func Factory(params object[] userParams) { - Handler.Release(); + return Handler.Factory(userParams); } /// - /// 释放服务 + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// + /// 关注的服务名 + /// 当服务发生重定义时调用的目标 + /// 方法名 + public static void Watch(string service, object target, string method) + { + Handler.Watch(service, target, method); + } + + /// + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 /// /// 服务名 - public static void Release() where TService : class + /// 当服务发生重定义时调用的目标 + /// 方法名 + public static void Watch(object target, string method) { - Handler.Release(); + Handler.Watch(target, method); } /// - /// 静态化一个服务,实例值会经过解决修饰器 + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 /// /// 服务名 - /// 实例值 - public static void Instance(object instance) where TService : class + /// 回调 + public static void Watch(Action method) { - Handler.Instance(instance); + Handler.Watch(method); + } + + /// + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// + /// 服务名 + /// 回调 + public static void Watch(Action method) + { + Handler.Watch(method); + } + + /// + /// 在回调区间内暂时性的静态化服务实例 + /// + /// 回调区间 + /// 服务名 + /// 实例名 + public static void Flash(Action callback, string service, object instance) + { + Handler.Flash(callback, service, instance); + } + + /// + /// 类型转为服务名 + /// + /// 服务类型 + /// 服务名 + public static string Type2Service() + { + return Handler.Type2Service(); } + #endregion } } \ No newline at end of file diff --git a/src/CatLib.Core/CatLib/Application.cs b/src/CatLib.Core/CatLib/Application.cs index 165a2ae..91c5cc4 100644 --- a/src/CatLib.Core/CatLib/Application.cs +++ b/src/CatLib.Core/CatLib/Application.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading; namespace CatLib @@ -23,7 +24,7 @@ public class Application : Container, IApplication /// /// 版本号 /// - private readonly Version version = new Version("1.1.0"); + private readonly Version version = new Version("1.2.0"); /// /// 框架启动流程 @@ -139,7 +140,7 @@ public Application() RegisterCoreAlias(); RegisterCoreService(); OnFindType(finder => { return Type.GetType(finder); }); - SetDebugLevel(DebugLevels.Prod); + DebugLevel = DebugLevels.Prod; } /// @@ -266,58 +267,80 @@ public int GetPriority(Type type, string method = null) } /// - /// 设定调试等级 + /// 调试等级 /// - /// 调试等级 - public void SetDebugLevel(DebugLevels level) + public DebugLevels DebugLevel { - Instance(Type2Service(typeof(DebugLevels)), level); + get + { + return (DebugLevels)Make(Type2Service(typeof(DebugLevels))); + } + set + { + Instance(Type2Service(typeof(DebugLevels)), value); + } } /// /// 触发一个事件,并获取事件的返回结果 /// /// 事件名称 - /// 载荷 + /// 载荷 /// 事件结果 - public object[] Trigger(string eventName, object payload = null) + public object[] Trigger(string eventName, params object[] payloads) { - return Dispatcher.Trigger(eventName, payload); + return Dispatcher.Trigger(eventName, payloads); } /// /// 触发一个事件,遇到第一个事件存在处理结果后终止,并获取事件的返回结果 /// /// 事件名 - /// 载荷 + /// 载荷 /// 事件结果 - public object TriggerHalt(string eventName, object payload = null) + public object TriggerHalt(string eventName, params object[] payloads) { - return Dispatcher.TriggerHalt(eventName, payload); + return Dispatcher.TriggerHalt(eventName, payloads); } /// - /// 注册一个事件 + /// 判断给定事件是否存在事件监听器 /// - /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - public IEventHandler On(string eventName, Action handler, int life = 0) + /// 事件名 + /// + /// 严格模式 + /// 启用严格模式则不使用正则来进行匹配事件监听器 + /// + /// 是否存在事件监听器 + public bool HasListeners(string eventName, bool strict = false) { - return Dispatcher.On(eventName, handler, life); + return Dispatcher.HasListeners(eventName, strict); } /// - /// 注册一个事件 + /// 注册一个事件监听器 /// /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - public IEventHandler Listen(string eventName, Func handler, int life = 0) + /// 调用目标 + /// 调用方法 + /// 事件对象 + public IEvent On(string eventName, object target, MethodInfo method) { - return Dispatcher.Listen(eventName, handler, life); + return Dispatcher.On(eventName, target, method); + } + + /// + /// 解除注册的事件监听器 + /// + /// + /// 事件解除目标 + /// 如果传入的是字符串(string)将会解除对应事件名的所有事件 + /// 如果传入的是事件对象(IEvent)那么解除对应事件 + /// 如果传入的是其他实例(object)会解除该实例下的所有事件 + /// + public void Off(object target) + { + Dispatcher.Off(target); } /// @@ -362,21 +385,10 @@ public int Compare(string version) return this.version.Compare(version); } - /// - /// 获取服务提供者基础类型 - /// - /// 服务提供者 - /// 基础类型 - private Type GetProviderBaseType(IServiceProvider provider) - { - var providerType = provider as IServiceProviderType; - return providerType == null ? provider.GetType() : providerType.BaseType; - } - /// /// 注册核心别名 /// - private void RegisterCoreAlias() + protected virtual void RegisterCoreAlias() { var application = Type2Service(typeof(Application)); Instance(application, this); @@ -394,9 +406,20 @@ private void RegisterCoreAlias() /// /// 注册核心服务 /// - private void RegisterCoreService() + protected virtual void RegisterCoreService() { this.Singleton().Alias(); } + + /// + /// 获取服务提供者基础类型 + /// + /// 服务提供者 + /// 基础类型 + private Type GetProviderBaseType(IServiceProvider provider) + { + var providerType = provider as IServiceProviderType; + return providerType == null ? provider.GetType() : providerType.BaseType; + } } } \ No newline at end of file diff --git a/src/CatLib.Core/CatLib/Facade.cs b/src/CatLib.Core/CatLib/Facade.cs index c424403..85e1a73 100644 --- a/src/CatLib.Core/CatLib/Facade.cs +++ b/src/CatLib.Core/CatLib/Facade.cs @@ -14,17 +14,103 @@ namespace CatLib /// /// 门面 /// - public abstract class Facade + public abstract class Facade { + /// + /// 实例 + /// + private static TService instance; + + /// + /// 绑定数据 + /// + private static IBindable binder; + + /// + /// 是否已经被初始化 + /// + private static bool inited; + + /// + /// 门面静态构造 + /// + static Facade() + { + if (!typeof(TService).IsInterface) + { + throw new RuntimeException("Facade<" + typeof(TService).Name + "> , generic type must be interface."); + } + + App.OnNewApplication += app => + { + instance = default(TService); + binder = null; + inited = false; + }; + } + /// /// 门面实例 /// - public static T Instance + public static TService Instance { get { - return App.Make(); + if (instance != null) + { + return instance; + } + + return Make(); } } + + /// + /// 初始化 + /// + private static TService Make() + { + if (!inited) + { + App.Watch(ServiceRebound); + inited = true; + } + + var newBinder = App.GetBind(); + if (newBinder == null || !newBinder.IsStatic) + { + return App.Make(); + } + + if (binder == null || binder != newBinder) + { + newBinder.OnRelease((oldBinder, __) => + { + if (oldBinder == binder) + { + instance = default(TService); + } + }); + binder = newBinder; + } + + return instance = App.Make(); + } + + /// + /// 当服务被重绑定时 + /// + /// 新的服务实例 + private static void ServiceRebound(TService service) + { + var newBinder = App.GetBind(); + if (newBinder == null || !newBinder.IsStatic) + { + instance = default(TService); + return; + } + + instance = service; + } } } \ No newline at end of file diff --git a/src/CatLib.Core/CatLib/IApplication.cs b/src/CatLib.Core/CatLib/IApplication.cs index a92b118..24e9626 100644 --- a/src/CatLib.Core/CatLib/IApplication.cs +++ b/src/CatLib.Core/CatLib/IApplication.cs @@ -79,9 +79,8 @@ public interface IApplication : IContainer, IDispatcher int GetPriority(Type type, string method = null); /// - /// 设定调试等级 + /// 调试等级 /// - /// 调试等级 - void SetDebugLevel(DebugLevels level); + DebugLevels DebugLevel { get; set; } } } diff --git a/src/CatLib.Core/Properties/AssemblyInfo.cs b/src/CatLib.Core/Properties/AssemblyInfo.cs index 4efe5f8..047c619 100644 --- a/src/CatLib.Core/Properties/AssemblyInfo.cs +++ b/src/CatLib.Core/Properties/AssemblyInfo.cs @@ -26,8 +26,8 @@ [assembly: Guid("4204658e-81fd-4106-a347-890cd369c8a4")] -[assembly: AssemblyVersion("1.1.4.0")] -[assembly: AssemblyFileVersion("1.1.4.0")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.0.0")] [assembly: InternalsVisibleTo("Assembly-CSharp-Editor"), InternalsVisibleTo("Assembly-CSharp-Editor-firstpass"), diff --git a/src/CatLib.Core/Support/Container/BindData.cs b/src/CatLib.Core/Support/Container/BindData.cs index 97f0a63..babe0e0 100644 --- a/src/CatLib.Core/Support/Container/BindData.cs +++ b/src/CatLib.Core/Support/Container/BindData.cs @@ -17,13 +17,8 @@ namespace CatLib /// /// 服务绑定数据 /// - internal sealed class BindData : IBindData + internal sealed class BindData : Bindable, IBindData { - /// - /// 当前绑定服务的服务名 - /// - public string Service { get; private set; } - /// /// 服务实现,执行这个委托将会获得服务实例 /// @@ -34,17 +29,6 @@ internal sealed class BindData : IBindData /// public bool IsStatic { get; private set; } - /// - /// 服务关系上下文 - /// 当前服务需求某个服务时可以指定给与什么服务 - /// - private Dictionary contextual; - - /// - /// 父级容器 - /// - private readonly Container container; - /// /// 服务构造修饰器 /// @@ -55,21 +39,6 @@ internal sealed class BindData : IBindData /// private List> release; - /// - /// 是否被释放 - /// - private bool isDestroy; - - /// - /// 同步锁 - /// - private readonly object syncRoot = new object(); - - /// - /// 给与数据 - /// - private GivenData given; - /// /// 构建一个绑定数据 /// @@ -77,43 +46,11 @@ internal sealed class BindData : IBindData /// 服务名 /// 服务实现 /// 服务是否是静态的 - internal BindData(Container container, string service, Func concrete, bool isStatic) + public BindData(Container container, string service, Func concrete, bool isStatic) + :base(container, service) { - this.container = container; - Service = service; Concrete = concrete; IsStatic = isStatic; - isDestroy = false; - } - - /// - /// 当需求某个服务 - /// - /// 服务名 - /// 绑定关系临时数据 - public IGivenData Needs(string service) - { - Guard.NotEmptyOrNull(service, "service"); - lock (syncRoot) - { - GuardIsDestroy(); - if (given == null) - { - given = new GivenData(container, this); - } - given.Needs(service); - } - return given; - } - - /// - /// 当需求某个服务 - /// - /// 服务类型 - /// 绑定关系临时数据 - public IGivenData Needs() - { - return Needs(container.Type2Service(typeof(T))); } /// @@ -123,7 +60,7 @@ public IGivenData Needs() /// 服务绑定数据 public IBindData Alias() { - return Alias(container.Type2Service(typeof(T))); + return Alias(Container.Type2Service(typeof(T))); } /// @@ -133,11 +70,11 @@ public IBindData Alias() /// 服务绑定数据 public IBindData Alias(string alias) { - lock (syncRoot) + lock (SyncRoot) { GuardIsDestroy(); Guard.NotEmptyOrNull(alias, "alias"); - container.Alias(alias, Service); + Container.Alias(alias, Service); return this; } } @@ -150,7 +87,7 @@ public IBindData Alias(string alias) public IBindData OnResolving(Func func) { Guard.NotNull(func, "func"); - lock (syncRoot) + lock (SyncRoot) { GuardIsDestroy(); if (resolving == null) @@ -174,7 +111,7 @@ public IBindData OnRelease(Action action) { throw new RuntimeException("Service [" + Service + "] is not Singleton(Static) Bind , Can not call OnRelease()."); } - lock (syncRoot) + lock (SyncRoot) { GuardIsDestroy(); if (release == null) @@ -189,29 +126,9 @@ public IBindData OnRelease(Action action) /// /// 移除绑定服务 , 在解除绑定时如果是静态化物体将会触发释放 /// - public void UnBind() - { - lock (syncRoot) - { - isDestroy = true; - container.UnBind(Service); - } - } - - /// - /// 获取上下文的需求关系 - /// - /// 需求的服务 - /// 给与的服务 - internal string GetContextual(string needs) + protected override void ReleaseBind() { - if (contextual == null) - { - return needs; - } - - string contextualNeeds; - return contextual.TryGetValue(needs, out contextualNeeds) ? contextualNeeds : needs; + Container.Unbind(this); } /// @@ -219,7 +136,7 @@ internal string GetContextual(string needs) /// /// 服务实例 /// 修饰后的服务实例 - internal object ExecResolvingDecorator(object obj) + internal object TriggerResolving(object obj) { if (resolving == null) { @@ -236,7 +153,7 @@ internal object ExecResolvingDecorator(object obj) /// 执行服务释放处理器 /// /// 服务实例 - internal void ExecReleaseDecorator(object obj) + internal void TriggerRelease(object obj) { if (release == null) { @@ -247,40 +164,5 @@ internal void ExecReleaseDecorator(object obj) action.Invoke(this, obj); } } - - /// - /// 为服务增加上下文 - /// - /// 需求什么服务 - /// 给与什么服务 - /// 服务绑定数据 - internal BindData AddContextual(string needs, string given) - { - lock (syncRoot) - { - GuardIsDestroy(); - if (contextual == null) - { - contextual = new Dictionary(); - } - if (contextual.ContainsKey(needs)) - { - throw new RuntimeException("Needs [" + needs + "] is already exist."); - } - contextual.Add(needs, given); - return this; - } - } - - /// - /// 守卫是否被释放 - /// - private void GuardIsDestroy() - { - if (isDestroy) - { - throw new RuntimeException("Current Instance has be mark Destroy."); - } - } } } \ No newline at end of file diff --git a/src/CatLib.Core/Support/Container/Bindable.cs b/src/CatLib.Core/Support/Container/Bindable.cs new file mode 100644 index 0000000..892fc45 --- /dev/null +++ b/src/CatLib.Core/Support/Container/Bindable.cs @@ -0,0 +1,176 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System.Collections.Generic; + +namespace CatLib +{ + /// + /// 可绑定对象 + /// + public abstract class Bindable : IBindable + { + /// + /// 当前绑定的名字 + /// + public string Service { get; private set; } + + /// + /// 父级容器 + /// + protected readonly Container Container; + + /// + /// 服务关系上下文 + /// 当前服务需求某个服务时可以指定给与什么服务 + /// + private Dictionary contextual; + + /// + /// 同步锁 + /// + protected readonly object SyncRoot = new object(); + + /// + /// 是否被释放 + /// + private bool isDestroy; + + /// + /// 构建一个绑定数据 + /// + /// 依赖注入容器 + /// 服务名 + protected Bindable(Container container, string service) + { + Container = container; + Service = service; + isDestroy = false; + } + + /// + /// 解除绑定 + /// + public void Unbind() + { + lock (SyncRoot) + { + isDestroy = true; + ReleaseBind(); + } + } + + /// + /// 为服务增加上下文 + /// + /// 需求什么服务 + /// 给与什么服务 + /// 服务绑定数据 + internal void AddContextual(string needs, string given) + { + lock (SyncRoot) + { + GuardIsDestroy(); + if (contextual == null) + { + contextual = new Dictionary(); + } + if (contextual.ContainsKey(needs)) + { + throw new RuntimeException("Needs [" + needs + "] is already exist."); + } + contextual.Add(needs, given); + } + } + + /// + /// 获取上下文的需求关系 + /// + /// 需求的服务 + /// 给与的服务 + internal string GetContextual(string needs) + { + if (contextual == null) + { + return needs; + } + string contextualNeeds; + return contextual.TryGetValue(needs, out contextualNeeds) ? contextualNeeds : needs; + } + + /// + /// 解除绑定 + /// + protected abstract void ReleaseBind(); + + /// + /// 守卫是否被释放 + /// + protected void GuardIsDestroy() + { + if (isDestroy) + { + throw new RuntimeException("Current bind has be mark Destroy."); + } + } + } + + /// + /// 可绑定对象 + /// + internal abstract class Bindable : Bindable, IBindable where TReturn : class, IBindable + { + /// + /// 给与数据 + /// + private GivenData given; + + /// + /// 构建一个绑定数据 + /// + /// 依赖注入容器 + /// 服务名 + protected Bindable(Container container, string service) + : base(container, service) + { + } + + /// + /// 当需求某个服务 + /// + /// 服务名 + /// 绑定关系临时数据 + public IGivenData Needs(string service) + { + Guard.NotEmptyOrNull(service, "service"); + lock (SyncRoot) + { + GuardIsDestroy(); + if (given == null) + { + given = new GivenData(Container, this); + } + given.Needs(service); + } + return given; + } + + /// + /// 当需求某个服务 + /// + /// 服务类型 + /// 绑定关系临时数据 + public IGivenData Needs() + { + return Needs(Container.Type2Service(typeof(T))); + } + } +} diff --git a/src/CatLib.Core/Support/Container/Container.cs b/src/CatLib.Core/Support/Container/Container.cs index e3db53c..80c3583 100644 --- a/src/CatLib.Core/Support/Container/Container.cs +++ b/src/CatLib.Core/Support/Container/Container.cs @@ -61,6 +61,26 @@ public class Container : IContainer /// private readonly SortSet, int> findType; + /// + /// 类型查询回调缓存 + /// + private readonly Dictionary findTypeCache; + + /// + /// 已经被解决过的服务名 + /// + private readonly HashSet resolved; + + /// + /// 重定义事件 + /// + private readonly Dictionary>> rebound; + + /// + /// 方法容器 + /// + private readonly MethodContainer methodContainer; + /// /// 同步锁 /// @@ -76,21 +96,56 @@ public class Container : IContainer /// private readonly Stack buildStack; + /// + /// 编译堆栈 + /// + protected Stack BuildStack + { + get { return buildStack; } + } + + /// + /// 用户参数堆栈 + /// + private readonly Stack userParamsStack; + + /// + /// 用户参数堆栈 + /// + protected Stack UserParamsStack + { + get { return userParamsStack; } + } + + /// + /// 是否在清空过程中 + /// + private bool flushing; + /// /// 构造一个容器 /// - public Container() + /// 初始预计服务数量 + public Container(int prime = 64) { - tags = new Dictionary>(); - aliases = new Dictionary(); - aliasesReverse = new Dictionary>(); - instances = new Dictionary(); - binds = new Dictionary(); - resolving = new List>(); - release = new List>(); + prime = Math.Max(8, prime); + tags = new Dictionary>((int)(prime * 0.25)); + aliases = new Dictionary(prime * 4); + aliasesReverse = new Dictionary>(prime * 4); + instances = new Dictionary(prime * 4); + binds = new Dictionary(prime * 4); + resolving = new List>((int)(prime * 0.25)); + release = new List>((int)(prime * 0.25)); + resolved = new HashSet(); findType = new SortSet, int>(); + findTypeCache = new Dictionary(prime * 4); + rebound = new Dictionary>>(prime); + buildStack = new Stack(32); + userParamsStack = new Stack(32); + injectTarget = typeof(InjectAttribute); - buildStack = new Stack(); + methodContainer = new MethodContainer(this, GetDependencies); + flushing = false; } /// @@ -109,16 +164,14 @@ public void Tag(string tag, params string[] service) lock (syncRoot) { + GuardFlushing(); List list; - if (tags.TryGetValue(tag, out list)) - { - list.AddRange(service); - } - else + if (!tags.TryGetValue(tag, out list)) { - list = new List(service); - tags.Add(tag, list); + tags[tag] = list = new List(); + } + list.AddRange(service); } } @@ -134,20 +187,19 @@ public object[] Tagged(string tag) Guard.NotEmptyOrNull(tag, "tag"); lock (syncRoot) { - List serviceList; - if (!tags.TryGetValue(tag, out serviceList)) + List services; + if (!tags.TryGetValue(tag, out services)) { throw new RuntimeException("Tag [" + tag + "] is not exist."); } - var result = new List(); - - foreach (var tagService in serviceList) + var result = new object[services.Count]; + for (var i = 0; i < services.Count; i++) { - result.Add(Make(tagService)); + result[i] = Make(services[i]); } - return result.ToArray(); + return result; } } @@ -162,7 +214,6 @@ public IBindData GetBind(string service) Guard.NotEmptyOrNull(service, "service"); lock (syncRoot) { - service = Normalize(service); service = AliasToService(service); BindData bindData; return binds.TryGetValue(service, out bindData) ? bindData : null; @@ -170,7 +221,7 @@ public IBindData GetBind(string service) } /// - /// 是否已经绑定了服务(只有进行过bind才视作绑定) + /// 是否已经绑定了服务 /// /// 服务名或别名 /// 服务是否被绑定 @@ -179,6 +230,58 @@ public bool HasBind(string service) return GetBind(service) != null; } + /// + /// 是否已经实例静态化 + /// + /// 服务名或别名 + /// 是否已经静态化 + public bool HasInstance(string service) + { + Guard.NotEmptyOrNull(service, "service"); + lock (syncRoot) + { + service = AliasToService(service); + return instances.ContainsKey(service); + } + } + + /// + /// 服务是否已经被解决过 + /// + /// 服务名或别名 + /// 是否已经被解决过 + public bool IsResolved(string service) + { + Guard.NotEmptyOrNull(service, "service"); + lock (syncRoot) + { + service = AliasToService(service); + return resolved.Contains(service) || instances.ContainsKey(service); + } + } + + /// + /// 是否可以生成服务 + /// + /// 服务名或者别名 + /// 是否可以生成服务 + public bool CanMake(string service) + { + Guard.NotEmptyOrNull(service, "service"); + lock (syncRoot) + { + service = AliasToService(service); + + if (HasBind(service) || HasInstance(service)) + { + return true; + } + + var type = SpeculatedServiceType(service); + return !IsBasicType(type) && !IsUnableType(type); + } + } + /// /// 服务是否是静态化的,如果服务不存在也将返回false /// @@ -191,7 +294,18 @@ public bool IsStatic(string service) } /// - /// 为服务设定一个别名 + /// 是否是别名 + /// + /// 名字 + /// 是否是别名 + public bool IsAlias(string name) + { + name = FormatService(name); + return aliases.ContainsKey(name); + } + + /// + /// 以全局的方式为服务设定一个别名 /// /// 别名 /// 映射到的服务名 @@ -208,15 +322,17 @@ public IContainer Alias(string alias, string service) throw new RuntimeException("Alias is Same as Service Name: [" + alias + "]."); } - alias = Normalize(alias); - service = Normalize(service); + alias = FormatService(alias); + service = FormatService(service); lock (syncRoot) { + GuardFlushing(); if (aliases.ContainsKey(alias)) { throw new RuntimeException("Alias [" + alias + "] is already exists."); } + if (!binds.ContainsKey(service) && !instances.ContainsKey(service)) { throw new RuntimeException("You must Bind() or Instance() serivce before you can call Alias()."); @@ -225,14 +341,11 @@ public IContainer Alias(string alias, string service) aliases.Add(alias, service); List serviceList; - if (aliasesReverse.TryGetValue(service, out serviceList)) - { - serviceList.Add(alias); - } - else + if (!aliasesReverse.TryGetValue(service, out serviceList)) { - aliasesReverse.Add(service, new List { alias }); + aliasesReverse[service] = serviceList = new List(); } + serviceList.Add(alias); } return this; @@ -244,11 +357,18 @@ public IContainer Alias(string alias, string service) /// 服务名 /// 服务实现 /// 服务是否是静态的 + /// 如果绑定失败则返回历史绑定对象 /// 服务绑定数据 - public IBindData BindIf(string service, Func concrete, bool isStatic) + public bool BindIf(string service, Func concrete, bool isStatic, out IBindData bindData) { var bind = GetBind(service); - return bind ?? Bind(service, concrete, isStatic); + if (bind == null && (HasInstance(service) || IsAlias(service))) + { + bindData = null; + return false; + } + bindData = bind ?? Bind(service, concrete, isStatic); + return bind == null; } /// @@ -257,11 +377,16 @@ public IBindData BindIf(string service, Func concr /// 服务名 /// 服务实现 /// 服务是否是静态的 + /// 如果绑定失败则返回历史绑定对象 /// 服务绑定数据 - public IBindData BindIf(string service, Type concrete, bool isStatic) + public bool BindIf(string service, Type concrete, bool isStatic, out IBindData bindData) { - var bind = GetBind(service); - return bind ?? Bind(service, concrete, isStatic); + if (IsUnableType(concrete)) + { + bindData = null; + return false; + } + return BindIf(service, WrapperTypeBuilder(service, concrete), isStatic, out bindData); } /// @@ -275,11 +400,11 @@ public IBindData BindIf(string service, Type concrete, bool isStatic) public IBindData Bind(string service, Type concrete, bool isStatic) { Guard.NotNull(concrete, "concrete"); - return Bind(service, (c, param) => + if (IsUnableType(concrete)) { - var container = (Container)c; - return container.BuildMake(service, concrete, false, param); - }, isStatic); + throw new RuntimeException("Bind type [" + concrete + "] can not built"); + } + return Bind(service, WrapperTypeBuilder(service, concrete), isStatic); } /// @@ -295,9 +420,11 @@ public IBindData Bind(string service, Func concret { Guard.NotEmptyOrNull(service, "service"); Guard.NotNull(concrete, "concrete"); - service = Normalize(service); + service = FormatService(service); lock (syncRoot) { + GuardFlushing(); + if (binds.ContainsKey(service)) { throw new RuntimeException("Bind [" + service + "] already exists."); @@ -316,48 +443,85 @@ public IBindData Bind(string service, Func concret var bindData = new BindData(this, service, concrete, isStatic); binds.Add(service, bindData); + if (IsResolved(service)) + { + if (isStatic) + { + // 如果为 静态的 那么直接解决这个服务 + // 在服务静态化的过程会触发 TriggerOnRebound + Resolve(service); + } + else + { + TriggerOnRebound(service); + } + } + return bindData; } } /// - /// 以依赖注入形式调用一个方法 + /// 绑定一个方法到容器 /// - /// 方法对象 /// 方法名 - /// 附加的参数 - /// 方法返回值 - /// ,null或者空字符串 - public object Call(object instance, string method, params object[] param) + /// 调用目标 + /// 调用方法 + /// 方法绑定数据 + public IMethodBind BindMethod(string method, object target, MethodInfo call) + { + GuardFlushing(); + return methodContainer.Bind(method, target, call); + } + + /// + /// 解除绑定的方法 + /// + /// + /// 解除目标 + /// 如果为字符串则作为调用方法名 + /// 如果为IMethodBind则作为指定方法 + /// 如果为其他对象则作为调用目标做全体解除 + /// + public void UnbindMethod(object target) { - Guard.NotNull(instance, "instance"); - Guard.NotEmptyOrNull(method, "method"); + methodContainer.Unbind(target); + } - var methodInfo = instance.GetType().GetMethod(method); - return Call(instance, methodInfo, param); + /// + /// 调用一个已经被绑定的方法 + /// + /// 方法名 + /// 用户提供的参数 + /// 调用结果 + public object Invoke(string method, params object[] userParams) + { + return methodContainer.Invoke(method, userParams); } /// /// 以依赖注入形式调用一个方法 /// - /// 方法对象 + /// 方法对象 /// 方法信息 - /// 方法参数 + /// 用户传入的参数 /// 方法返回值 - /// ,null - public object Call(object instance, MethodInfo methodInfo, params object[] param) + /// ,null + public object Call(object target, MethodInfo methodInfo, params object[] userParams) { - Guard.NotNull(instance, "instance"); - Guard.NotNull(methodInfo, "methodInfo"); + Guard.Requires(methodInfo != null); + if (!methodInfo.IsStatic) + { + Guard.Requires(target != null); + } - var type = instance.GetType(); - var parameter = new List(methodInfo.GetParameters()); + var parameter = methodInfo.GetParameters(); lock (syncRoot) { - var bindData = GetBindData(Type2Service(type)); - param = parameter.Count > 0 ? GetDependencies(bindData, parameter, param) : new object[] { }; - return methodInfo.Invoke(instance, param); + var bindData = GetBindFillable(target != null ? Type2Service(target.GetType()) : null); + userParams = GetDependencies(bindData, parameter, userParams) ?? new object[] { }; + return methodInfo.Invoke(target, userParams); } } @@ -365,62 +529,34 @@ public object Call(object instance, MethodInfo methodInfo, params object[] param /// 构造服务 /// /// 服务名或别名 - /// 构造参数 - /// 服务实例,如果构造失败那么返回null + /// 用户传入的构造参数 /// null或者空字符串 /// 出现循环依赖 /// 服务实例,如果构造失败那么返回null - public object MakeWith(string service, params object[] param) + public object Make(string service, params object[] userParams) { - Guard.NotEmptyOrNull(service, "service"); - lock (syncRoot) - { - service = Normalize(service); - service = AliasToService(service); - - object instance; - if (instances.TryGetValue(service, out instance)) - { - return instance; - } - - if (buildStack.Contains(service)) - { - throw new RuntimeException("Circular dependency detected while for [" + service + "]."); - } - - buildStack.Push(service); - try - { - return BuildMake(service, null, true, param); - } - finally - { - buildStack.Pop(); - } - } + return Resolve(service, userParams); } /// /// 构造服务 /// - /// 服务名或别名 - /// null或者空字符串 - /// 出现循环依赖 + /// 服务名或者别名 /// 服务实例,如果构造失败那么返回null - public object Make(string service) + public object this[string service] { - return MakeWith(service); + get { return Make(service); } } /// - /// 构造服务 + /// 获取一个回调,当执行回调可以生成指定的服务 /// - /// 服务名或者别名 - /// 服务实例,如果构造失败那么返回null - public object this[string service] + /// 服务名或别名 + /// 用户传入的参数 + /// 回调方案 + public Func Factory(string service, params object[] userParams) { - get { return Make(service); } + return () => Make(service, userParams); } /// @@ -430,12 +566,13 @@ public object this[string service] /// 服务实例,null也是合法的实例值 /// null或者空字符串 /// 的服务在绑定设置中不是静态的 - public void Instance(string service, object instance) + /// 被修饰器处理后的新的实例 + public object Instance(string service, object instance) { Guard.NotEmptyOrNull(service, "service"); lock (syncRoot) { - service = Normalize(service); + GuardFlushing(); service = AliasToService(service); var bindData = GetBind(service); @@ -445,17 +582,26 @@ public void Instance(string service, object instance) { throw new RuntimeException("Service [" + service + "] is not Singleton(Static) Bind."); } - instance = ((BindData)bindData).ExecResolvingDecorator(instance); + instance = ((BindData)bindData).TriggerResolving(instance); } else { bindData = MakeEmptyBindData(service); } + var isResolved = IsResolved(service); + Release(service); - instance = ExecOnResolvingDecorator(bindData, instance); + instance = TriggerOnResolving(bindData, instance); instances.Add(service, instance); + + if (isResolved) + { + TriggerOnRebound(service, instance); + } + + return instance; } } @@ -468,7 +614,6 @@ public void Release(string service) Guard.NotEmptyOrNull(service, "service"); lock (syncRoot) { - service = Normalize(service); service = AliasToService(service); object instance; @@ -477,42 +622,14 @@ public void Release(string service) return; } - var bindData = GetBindData(service); - bindData.ExecReleaseDecorator(instance); - ExecOnReleaseDecorator(bindData, instance); + var bindData = GetBindFillable(service); + bindData.TriggerRelease(instance); + TriggerOnRelease(bindData, instance); + DisposeInstance(instance); instances.Remove(service); } } - /// - /// 清空容器的所有实例,绑定,别名,标签,解决器 - /// - public void Flush() - { - lock (syncRoot) - { - var releaseList = new string[instances.Count]; - var i = 0; - foreach (var instance in instances) - { - releaseList[i++] = instance.Key; - } - foreach (var service in releaseList) - { - Release(service); - } - - binds.Clear(); - instances.Clear(); - aliases.Clear(); - aliasesReverse.Clear(); - tags.Clear(); - resolving.Clear(); - release.Clear(); - findType.Clear(); - } - } - /// /// 当查找类型无法找到时会尝试去调用开发者提供的查找类型函数 /// @@ -524,6 +641,7 @@ public IContainer OnFindType(Func finder, int priority = int.MaxVa Guard.NotNull(finder, "finder"); lock (syncRoot) { + GuardFlushing(); findType.Add(finder, priority); } return this; @@ -532,14 +650,15 @@ public IContainer OnFindType(Func finder, int priority = int.MaxVa /// /// 当静态服务被释放时 /// - /// 处理释放时的回调 + /// 处理释放时的回调 /// 当前容器实例 - public IContainer OnRelease(Action action) + public IContainer OnRelease(Action callback) { - Guard.NotNull(action, "action"); + Guard.NotNull(callback, "callback"); lock (syncRoot) { - release.Add(action); + GuardFlushing(); + release.Add(callback); } return this; } @@ -547,213 +666,610 @@ public IContainer OnRelease(Action action) /// /// 当服务被解决时,生成的服务会经过注册的回调函数 /// - /// 回调函数 + /// 回调函数 /// 当前容器对象 - public IContainer OnResolving(Func func) + public IContainer OnResolving(Func callback) { - Guard.NotNull(func, "func"); + Guard.NotNull(callback, "callback"); lock (syncRoot) { - resolving.Add(func); - - var result = new Dictionary(); - foreach (var data in instances) - { - var bindData = GetBindData(data.Key); - result[data.Key] = func.Invoke(bindData, data.Value); - } - foreach (var data in result) - { - instances[data.Key] = data.Value; - } - result.Clear(); + GuardFlushing(); + resolving.Add(callback); } return this; } /// - /// 解除绑定服务 + /// 当一个已经被解决的服务发生重定义时触发 /// - /// 服务名或者别名 - internal void UnBind(string service) + /// 服务名 + /// 回调 + /// 服务容器 + public IContainer OnRebound(string service, Action callback) { + Guard.NotNull(callback, "callback"); lock (syncRoot) { - service = Normalize(service); + GuardFlushing(); service = AliasToService(service); - Release(service); - List serviceList; - if (aliasesReverse.TryGetValue(service, out serviceList)) + List> list; + if (!rebound.TryGetValue(service, out list)) { - foreach (var alias in serviceList) - { - aliases.Remove(alias); - } - aliasesReverse.Remove(service); + rebound[service] = list = new List>(); } - binds.Remove(service); + list.Add(callback); } + return this; } /// - /// 将类型转为服务名 + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// 服务的新建(第一次解决服务)操作并不会触发重定义 /// - /// 类型 - /// 服务名 - public string Type2Service(Type type) + /// 关注的服务名 + /// 当服务发生重定义时调用的目标 + /// 方法信息 + public void Watch(string service, object target, MethodInfo methodInfo) { - return type.ToString(); - } + Guard.Requires(methodInfo != null); - /// - /// 执行全局解决修饰器 - /// - /// 服务绑定数据 - /// 服务实例 - /// 被修饰器修饰后的服务实例 - private object ExecOnResolvingDecorator(IBindData bindData, object obj) - { - foreach (var func in resolving) + if (!methodInfo.IsStatic) { - obj = func(bindData, obj); + Guard.Requires(target != null); } - return obj; + + OnRebound(service, (instance) => + { + Call(target, methodInfo, instance); + }); } /// - /// 执行全局释放修饰器 + /// 解除绑定服务 /// - /// 服务绑定数据 - /// 服务实例 - /// 被修饰器修饰后的服务实例 - private void ExecOnReleaseDecorator(IBindData bindData, object obj) + /// 服务名或者别名 + public void Unbind(string service) { - foreach (var action in release) + service = AliasToService(service); + var bind = GetBind(service); + if (bind != null) { - action.Invoke(bindData, obj); + bind.Unbind(); } } /// - /// 构造服务 + /// 清空容器的所有实例,绑定,别名,标签,解决器 /// - /// 服务名 - /// 服务类型 - /// 是否直接调用自Make函数 - /// 构造参数 - /// 服务实例 - private object BuildMake(string makeService, Type makeServiceType, bool isFromMake, params object[] param) + public virtual void Flush() { - var bindData = GetBindData(makeService); - var buildInstance = isFromMake ? BuildUseConcrete(bindData, makeServiceType, param) : Build(bindData, makeServiceType ?? GetType(bindData.Service), param); - - //只有是来自于make函数的调用时才执行di,包装,以及修饰 - if (!isFromMake) + lock (syncRoot) { - return buildInstance; - } - - AttrInject(bindData, buildInstance); + try + { + flushing = true; + foreach (var service in Dict.Keys(instances)) + { + Release(service); + } - if (bindData.IsStatic) - { - Instance(makeService, buildInstance); - } - else - { - buildInstance = ExecOnResolvingDecorator(bindData, bindData.ExecResolvingDecorator(buildInstance)); + tags.Clear(); + aliases.Clear(); + aliasesReverse.Clear(); + instances.Clear(); + binds.Clear(); + resolving.Clear(); + release.Clear(); + resolved.Clear(); + findType.Clear(); + findTypeCache.Clear(); + buildStack.Clear(); + userParamsStack.Clear(); + rebound.Clear(); + methodContainer.Flush(); + } + finally + { + flushing = false; + } } - - return buildInstance; } /// - /// 常规编译一个服务 + /// 将类型转为服务名 /// - /// 服务绑定数据 - /// 服务类型 - /// 构造参数 - /// 服务实例 - private object BuildUseConcrete(BindData makeServiceBindData, Type makeServiceType, object[] param) + /// 类型 + /// 服务名 + public virtual string Type2Service(Type type) { - return makeServiceBindData.Concrete != null ? - makeServiceBindData.Concrete(this, param) : - BuildMake(makeServiceBindData.Service, makeServiceType, false, param); + return type.ToString(); } /// - /// 构造服务 - 实现 + /// 解除绑定服务 /// - /// 服务绑定数据 - /// 服务类型 - /// 构造参数 - /// 服务实例 - private object Build(BindData makeServiceBindData, Type makeServiceType, object[] param) + /// 绑定关系 + internal void Unbind(IBindable bindable) { - param = param ?? new object[] { }; - - if (makeServiceType == null) + lock (syncRoot) { - return null; + Release(bindable.Service); + List serviceList; + if (aliasesReverse.TryGetValue(bindable.Service, out serviceList)) + { + foreach (var alias in serviceList) + { + aliases.Remove(alias); + } + aliasesReverse.Remove(bindable.Service); + } + binds.Remove(bindable.Service); } + } - if (makeServiceType.IsAbstract || makeServiceType.IsInterface) + /// + /// 在回调区间内暂时性的静态化服务实例 + /// + /// 回调区间 + /// 服务映射 + public void Flash(Action callback, params KeyValuePair[] services) + { + lock (syncRoot) { - return null; - } + if (services == null || services.Length <= 0) + { + callback(); + return; + } - var constructor = makeServiceType.GetConstructors(); - if (constructor.Length <= 0) - { + Stack> serviceStack = null; try { - return Activator.CreateInstance(makeServiceType); + foreach (var service in services) + { + try + { + // 如果服务被绑定过了,那么我们认为这不是一个Flash可用的服务 + // 所以我们抛出一个异常来终止该操作。 + if (HasBind(service.Key)) + { + throw new RuntimeException("Flash service [" + service.Key + + "] name has be used for bind or alias."); + } + } + catch + { + // 如果在 HasBind 执行过程中出现了异常,那么清空服务堆栈 + // 因为服务还没替换也无需在执行还原操作。 + serviceStack = null; + throw; + } + + if (!HasInstance(service.Key)) + { + continue; + } + + // 如果服务已经存在那么,将旧的服务加入堆栈。 + // 等待Flash操作完成后再恢复旧的服务实例。 + serviceStack = serviceStack ?? new Stack>(services.Length); + serviceStack.Push(new KeyValuePair(service.Key, Make(service.Key))); + } + + Arr.Flash(services, + service => Instance(service.Key, service.Value), + service => Release(service.Key), + callback); } - catch (Exception ex) + finally { - throw ThrowBuildFaild(makeServiceType, ex); + while (serviceStack != null && serviceStack.Count > 0) + { + var service = serviceStack.Pop(); + Instance(service.Key, service.Value); + } } } + } - var parameter = new List(constructor[constructor.Length - 1].GetParameters()); + /// + /// 是否是依赖注入容器默认的基础类型 + /// + /// 基础类型 + /// 是否是基础类型 + protected virtual bool IsBasicType(Type type) + { + return type == null || type.IsPrimitive || type == typeof(string); + } - if (parameter.Count > 0) + /// + /// 是否是无法被构建的类型 + /// + /// 类型 + /// 是否可以被构建 + protected virtual bool IsUnableType(Type type) + { + return type == null || type.IsAbstract || type.IsInterface || type.IsArray || type.IsEnum; + } + + /// + /// 包装一个类型,可以被用来生成服务 + /// + /// 服务名 + /// 类型 + /// 根据类型生成的服务 + protected virtual Func WrapperTypeBuilder(string service, Type concrete) + { + service = FormatService(service); + return (container, userParams) => ((Container)container).CreateInstance(GetBindFillable(service), concrete, userParams); + } + + /// + /// 从用户传入的参数中获取依赖 + /// + /// 基础参数 + /// 用户传入参数 + /// 合适的注入参数 + protected virtual object GetDependenciesFromUserParams(ParameterInfo baseParam, ref object[] userParams) + { + if (userParams == null) { - param = GetDependencies(makeServiceBindData, parameter, param); + return null; } + GuardUserParamsCount(userParams.Length); + + for (var n = 0; n < userParams.Length; n++) + { + var userParam = userParams[n]; + + if (ChangeType(ref userParam, baseParam.ParameterType)) + { + Arr.RemoveAt(ref userParams, n); + return userParam; + } + } + + return null; + } + + /// + /// 转换参数类型 + /// + /// 需要转换的参数 + /// 转换到的类型 + /// 是否转换成功 + protected virtual bool ChangeType(ref object result, Type conversionType) + { try { - return Activator.CreateInstance(makeServiceType, param); + if (result == null || conversionType.IsInstanceOfType(result)) + { + return true; + } + + if (IsBasicType(result.GetType()) && typeof(IVariant).IsAssignableFrom(conversionType)) + { + try + { + result = Make(Type2Service(conversionType), result); + return true; + } + catch (Exception) + { + // ignored + // when throw exception then stop inject + } + } + + if (result is IConvertible && typeof(IConvertible).IsAssignableFrom(conversionType)) + { + result = Convert.ChangeType(result, conversionType); + return true; + } } - catch (Exception ex) + catch (Exception) + { + // ignored + // when throw exception then stop inject + } + + return false; + } + + /// + /// 获取字段需求服务 + /// + /// 字段 + /// 需求的服务名 + protected virtual string GetPropertyNeedsService(PropertyInfo property) + { + var injectAttr = (InjectAttribute)property.GetCustomAttributes(injectTarget, false)[0]; + return string.IsNullOrEmpty(injectAttr.Alias) + ? Type2Service(property.PropertyType) + : injectAttr.Alias; + } + + /// + /// 获取参数需求服务 + /// + /// 当前正在解决的变量 + /// 需求的服务名 + protected virtual string GetParamNeedsService(ParameterInfo baseParam) + { + var needService = Type2Service(baseParam.ParameterType); + if (!baseParam.IsDefined(injectTarget, false)) + { + return needService; + } + + var injectAttr = (InjectAttribute)baseParam.GetCustomAttributes(injectTarget, false)[0]; + if (!string.IsNullOrEmpty(injectAttr.Alias)) + { + needService = injectAttr.Alias; + } + return needService; + } + + /// + /// 解决基本类型 + /// + /// 请求注入操作的服务绑定数据 + /// 希望解决的服务名或者别名 + /// 当前正在解决的变量 + /// 解决结果 + protected virtual object ResolveAttrPrimitive(Bindable makeServiceBindData, string service, PropertyInfo baseParam) + { + service = makeServiceBindData.GetContextual(service); + if (CanMake(service)) { - throw ThrowBuildFaild(makeServiceType, ex); + return Make(service); } + + var result = SpeculationServiceByParamName(makeServiceBindData, baseParam.Name, baseParam.PropertyType); + if (result != null) + { + return result; + } + + throw MakeUnresolvablePrimitiveException(baseParam.Name, baseParam.DeclaringType); } /// - /// 触发编译异常 + /// 解决类类型 /// + /// 请求注入操作的服务绑定数据 + /// 希望解决的服务名或者别名 + /// 当前正在解决的变量 + /// 解决结果 + protected virtual object ResloveAttrClass(Bindable makeServiceBindData, string service, PropertyInfo baseParam) + { + try + { + return Make(makeServiceBindData.GetContextual(service)); + } + catch (Exception) + { + var result = SpeculationServiceByParamName(makeServiceBindData, baseParam.Name, baseParam.PropertyType); + if (result != null) + { + return result; + } + + throw; + } + } + + /// + /// 解决基本类型 + /// + /// 请求注入操作的服务绑定数据 + /// 希望解决的服务名或者别名 + /// 当前正在解决的变量 + /// 解决结果 + protected virtual object ResolvePrimitive(Bindable makeServiceBindData, string service, ParameterInfo baseParam) + { + service = makeServiceBindData.GetContextual(service); + if (CanMake(service)) + { + return Make(service); + } + + var result = SpeculationServiceByParamName(makeServiceBindData, baseParam.Name, baseParam.ParameterType); + if (result != null) + { + return result; + } + + if (baseParam.IsOptional) + { + return baseParam.DefaultValue; + } + + throw MakeUnresolvablePrimitiveException(baseParam.Name, baseParam.Member.DeclaringType); + } + + /// + /// 解决类类型 + /// + /// 请求注入操作的服务绑定数据 + /// 希望解决的服务名或者别名 + /// 当前正在解决的变量 + /// 解决结果 + protected virtual object ResloveClass(Bindable makeServiceBindData, string service, ParameterInfo baseParam) + { + try + { + return Make(makeServiceBindData.GetContextual(service)); + } + catch (UnresolvableException) + { + var result = SpeculationServiceByParamName(makeServiceBindData, baseParam.Name, baseParam.ParameterType); + if (result != null) + { + return result; + } + + if (baseParam.IsOptional) + { + return baseParam.DefaultValue; + } + + throw; + } + } + + /// + /// 根据参数名字来推测服务 + /// + /// 请求注入操作的服务绑定数据 + /// 参数名 + /// 参数类型 + /// 推测的服务 + protected virtual object SpeculationServiceByParamName(Bindable makeServiceBindData, string paramName, Type paramType) + { + var service = makeServiceBindData.GetContextual("@" + paramName); + + if (!CanMake(service)) + { + return null; + } + + var instance = Make(service); + return ChangeType(ref instance, paramType) ? instance : null; + } + + /// + /// 获取编译堆栈调试消息 + /// + /// + protected virtual string GetBuildStackDebugMessage() + { + var previous = string.Join(", ", BuildStack.ToArray()); + return " While building [" + previous + "]."; + } + + /// + /// 生成一个编译失败异常 + /// + /// 构造的服务名字 /// 构造的服务类型 - /// 异常 + /// 内部异常 + /// 运行时异常 + protected virtual UnresolvableException MakeBuildFaildException(string makeService, Type makeServiceType, Exception innerException) + { + string message; + if (makeServiceType != null) + { + message = "Target [" + makeServiceType + "] build faild. Service is [" + makeService + "]."; + } + else + { + message = "Service [" + makeService + "] is not exists."; + } + + message += GetBuildStackDebugMessage(); + return new UnresolvableException(message, innerException); + } + + /// + /// 生成一个未能解决基本类型的异常 + /// + /// 变量名 + /// 变量所属类 /// 运行时异常 - private RuntimeException ThrowBuildFaild(Type makeServiceType, Exception ex) + protected virtual UnresolvableException MakeUnresolvablePrimitiveException(string name, Type declaringClass) { - return new RuntimeException("Build [" + makeServiceType + "] faild", ex); + var message = "Unresolvable primitive dependency , resolving [" + name + "] in class [" + declaringClass + "]"; + return new UnresolvableException(message); } /// - /// 标准化服务名 + /// 生成一个出现循环依赖的异常 + /// + /// 当前服务名 + /// 运行时异常 + protected virtual RuntimeException MakeCircularDependencyException(string service) + { + var message = "Circular dependency detected while for [" + service + "]."; + message += GetBuildStackDebugMessage(); + return new RuntimeException(message); + } + + /// + /// 格式化服务名 /// /// 服务名 - /// 标准化后的服务名 - private string Normalize(string service) + /// 格式化后的服务名 + protected virtual string FormatService(string service) { return service.Trim(); } + /// + /// 检查实例是否实现自某种类型 + /// + /// 需要实现自的类型 + /// 生成的实例 + /// 是否符合类型 + protected virtual bool CanInject(Type type, object instance) + { + return instance == null || type.IsInstanceOfType(instance); + } + + /// + /// 保证用户传入参数必须小于指定值 + /// + /// 传入参数数量 + protected virtual void GuardUserParamsCount(int count) + { + if (count > 255) + { + throw new RuntimeException("Too many parameters , must be less or equal than 255"); + } + } + + /// + /// 守卫解决实例状态 + /// + /// 服务实例 + /// 服务名 + protected virtual void GuardResolveInstance(object instance, string makeService) + { + if (instance == null) + { + throw MakeBuildFaildException(makeService, SpeculatedServiceType(makeService), null); + } + } + + /// + /// 根据服务名推测服务的类型 + /// + /// 服务名 + /// 服务类型 + protected virtual Type SpeculatedServiceType(string service) + { + Type result; + + if (findTypeCache.TryGetValue(service, out result)) + { + return result; + } + + foreach (var finder in findType) + { + var type = finder.Invoke(service); + if (type != null) + { + return findTypeCache[service] = type; + } + } + + return findTypeCache[service] = null; + } + /// /// 属性注入 /// @@ -761,7 +1277,7 @@ private string Normalize(string service) /// 服务实例 /// 服务实例 /// 属性是必须的或者注入类型和需求类型不一致 - private void AttrInject(BindData makeServiceBindData, object makeServiceInstance) + protected virtual void AttributeInject(Bindable makeServiceBindData, object makeServiceInstance) { if (makeServiceInstance == null) { @@ -770,38 +1286,28 @@ private void AttrInject(BindData makeServiceBindData, object makeServiceInstance foreach (var property in makeServiceInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { - if (!property.CanWrite) + if (!property.CanWrite + || !property.IsDefined(injectTarget, false)) { continue; } - if (!property.IsDefined(injectTarget, false)) - { - continue; - } + var needService = GetPropertyNeedsService(property); - var injectAttr = (InjectAttribute)property.GetCustomAttributes(injectTarget, false)[0]; - var needService = string.IsNullOrEmpty(injectAttr.Alias) - ? Type2Service(property.PropertyType) - : injectAttr.Alias; object instance; - if (property.PropertyType.IsClass || property.PropertyType.IsInterface) + if (property.PropertyType.IsClass + || property.PropertyType.IsInterface) { - instance = ResloveClassAttr(makeServiceBindData, needService); + instance = ResloveAttrClass(makeServiceBindData, needService, property); } else { - instance = ResolveNonClassAttr(makeServiceBindData, needService); + instance = ResolveAttrPrimitive(makeServiceBindData, needService, property); } - if (injectAttr.Required && instance == null) + if (!CanInject(property.PropertyType, instance)) { - throw new RuntimeException("[" + makeServiceBindData.Service + "] Attr [" + property.PropertyType + "] Required [" + needService + "] Service."); - } - - if (instance != null && !property.PropertyType.IsInstanceOfType(instance)) - { - throw new RuntimeException("[" + makeServiceBindData.Service + "] Attr inject type must be [" + property.PropertyType + "] , But instance is [" + instance.GetType() + "] , Make service is [" + needService + "]."); + throw new UnresolvableException("[" + makeServiceBindData.Service + "] Attr inject type must be [" + property.PropertyType + "] , But instance is [" + instance.GetType() + "] , Make service is [" + needService + "]."); } property.SetValue(makeServiceInstance, instance, null); @@ -809,175 +1315,464 @@ private void AttrInject(BindData makeServiceBindData, object makeServiceInstance } /// - /// 解决非类类型 + /// 检查是否可以紧缩注入用户传入的参数 /// - /// 请求注入操作的服务绑定数据 - /// 希望构造的服务名或者别名 - /// 解决结果 - private object ResolveNonClassAttr(BindData makeServiceBindData, string service) + /// 服务实例的参数信息 + /// 输入的构造参数列表 + /// 是否可以紧缩注入 + protected virtual bool CheckCompactInjectUserParams(ParameterInfo baseParam, object[] userParams) { - return Make(makeServiceBindData.GetContextual(service)); + if (userParams == null || userParams.Length <= 0) + { + return false; + } + + return baseParam.ParameterType == typeof(object[]) + || baseParam.ParameterType == typeof(object); } /// - /// 解决类类型 + /// 获取通过紧缩注入的参数 /// - /// 请求注入操作的服务绑定数据 - /// 希望构造的服务名或者别名 - /// 解决结果 - private object ResloveClassAttr(BindData makeServiceBindData, string service) + /// 服务实例的参数信息 + /// 输入的构造参数列表 + /// 紧缩注入的参数 + protected virtual object GetCompactInjectUserParams(ParameterInfo baseParam, ref object[] userParams) { - return Make(makeServiceBindData.GetContextual(service)); + if (!CheckCompactInjectUserParams(baseParam, userParams)) + { + return null; + } + + var result = userParams; + userParams = null; + + if (baseParam.ParameterType == typeof(object) + && result != null && result.Length == 1) + { + return result[0]; + } + + return result; + } + + /// + /// 获取参数()匹配器 + /// 开发者重写后可以实现自己的匹配器 + /// 如果调用获取到的匹配器后返回结果为null则表示没有匹配到参数 + /// + /// 用户传入的参数 + /// 匹配器,如果返回null则表示没有匹配器 + protected virtual Func GetParamsMatcher(ref object[] userParams) + { + if (userParams == null || userParams.Length <= 0) + { + return null; + } + + var tables = GetParamsTypeInUserParams(ref userParams); + return tables.Length <= 0 ? null : MakeParamsMatcher(tables); } /// /// 获取依赖解决结果 /// /// 服务绑定数据 - /// 服务实例的参数信息 - /// 输入的构造参数列表 + /// 服务实例的参数信息 + /// 输入的构造参数列表 /// 服务所需参数的解决结果 /// 生成的实例类型和需求类型不一致 - private object[] GetDependencies(BindData makeServiceBindData, IList paramInfo, IList param) + protected virtual object[] GetDependencies(Bindable makeServiceBindData, ParameterInfo[] baseParams, object[] userParams) { - var myParam = new List(); + if (baseParams.Length <= 0) + { + return null; + } - for (var i = 0; i < paramInfo.Count; i++) + var results = new List(baseParams.Length); + + // 获取一个参数匹配器用于筛选参数 + var matcher = GetParamsMatcher(ref userParams); + + foreach (var baseParam in baseParams) { - var info = paramInfo[i]; - if (param != null && i < param.Count) + // 使用参数匹配器对参数进行匹配,参数匹配器是最先进行的,因为他们的匹配精度是最准确的 + var param = (matcher == null) ? null : matcher(baseParam); + + // 当容器发现开发者使用 object 或者 object[] 作为参数类型时 + // 我们尝试将所有用户传入的用户参数紧缩注入 + param = param ?? GetCompactInjectUserParams(baseParam, ref userParams); + + // 从用户传入的参数中挑选合适的参数,按照相对顺序依次注入 + param = param ?? GetDependenciesFromUserParams(baseParam, ref userParams); + + string needService = null; + + if (param == null) { - if (info.ParameterType.IsInstanceOfType(param[i])) + // 尝试通过依赖注入容器来生成所需求的参数 + needService = GetParamNeedsService(baseParam); + + if (baseParam.ParameterType.IsClass + || baseParam.ParameterType.IsInterface) { - myParam.Add(param[i]); - continue; + param = ResloveClass(makeServiceBindData, needService, baseParam); + } + else + { + param = ResolvePrimitive(makeServiceBindData, needService, baseParam); } + } - try + // 对筛选到的参数进行注入检查 + if (!CanInject(baseParam.ParameterType, param)) + { + var error = "[" + makeServiceBindData.Service + "] Params inject type must be [" + + baseParam.ParameterType + "] , But instance is [" + param.GetType() + "]"; + if (needService == null) { - myParam.Add(Convert.ChangeType(param[i], info.ParameterType)); + error += " Inject params from user incoming parameters."; } - catch (Exception ex) + else { - throw new RuntimeException( - string.Format("Params [{0}({1})] can not convert to [{2}] , Service [{3}]", - info.Name, param[i], info.ParameterType, makeServiceBindData.Service - ), ex); + error += " Make service is [" + needService + "]."; } - continue; + + throw new UnresolvableException(error); } - var needService = Type2Service(info.ParameterType); - InjectAttribute injectAttr = null; - if (info.IsDefined(injectTarget, false)) + results.Add(param); + } + + return results.ToArray(); + } + + /// + /// 获取构造函数参数 + /// + /// 服务绑定数据 + /// 服务类型 + /// 用户传入的构造参数 + /// 构造函数参数 + protected virtual object[] GetConstructorsInjectParams(Bindable makeServiceBindData, Type makeServiceType, object[] userParams) + { + var constructors = makeServiceType.GetConstructors(); + if (constructors.Length <= 0) + { + return null; + } + + Exception exception = null; + foreach (var constructor in constructors) + { + try + { + return GetDependencies(makeServiceBindData, constructor.GetParameters(), userParams); + } + catch (Exception ex) { - var propertyAttrs = info.GetCustomAttributes(injectTarget, false); - if (propertyAttrs.Length > 0) + if (exception == null) { - injectAttr = (InjectAttribute)propertyAttrs[0]; - if (!string.IsNullOrEmpty(injectAttr.Alias)) - { - needService = injectAttr.Alias; - } + exception = ex; } } + } - object instance; - if (info.ParameterType.IsClass || info.ParameterType.IsInterface) + Guard.Requires(exception != null); + throw exception; + } + + /// + /// 验证重置状态 + /// + private void GuardFlushing() + { + if (flushing) + { + throw new RuntimeException("Container is flushing can not "); + } + } + + /// + /// 获取别名最终对应的服务名 + /// + /// 服务名或别名 + /// 最终映射的服务名 + private string AliasToService(string service) + { + service = FormatService(service); + string alias; + return aliases.TryGetValue(service, out alias) ? alias : service; + } + + /// + /// 触发全局解决修饰器 + /// + /// 服务绑定数据 + /// 服务实例 + /// 被修饰器修饰后的服务实例 + private object TriggerOnResolving(IBindData bindData, object obj) + { + foreach (var func in resolving) + { + obj = func(bindData, obj); + } + return obj; + } + + /// + /// 触发全局释放修饰器 + /// + /// 服务绑定数据 + /// 服务实例 + /// 被修饰器修饰后的服务实例 + private void TriggerOnRelease(IBindData bindData, object obj) + { + foreach (var action in release) + { + action.Invoke(bindData, obj); + } + } + + /// + /// 触发服务重定义事件 + /// + /// 发生重定义的服务 + /// 服务实例(如果为空将会从容器请求) + private void TriggerOnRebound(string service, object instance = null) + { + var callbacks = GetOnReboundCallbacks(service); + if (callbacks == null || callbacks.Count <= 0) + { + return; + } + + var bind = GetBind(service); + instance = instance ?? Make(service); + Flash(() => + { + for (var index = 0; index < callbacks.Count; index++) { - instance = ResloveClass(makeServiceBindData, needService); + callbacks[index](instance); + // 如果是非实例绑定那么每个 callback 给定单独的实例 + if (index + 1 < callbacks.Count && (bind == null || !bind.IsStatic)) + { + instance = Make(service); + } } - else + }, Pair(typeof(IBindData), bind), + Pair(typeof(BindData), bind)); + } + + /// + /// 释放实例 + /// + /// 实例 + private void DisposeInstance(object obj) + { + var disposable = obj as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + } + + /// + /// 类型配实例配偶 + /// + /// 类型 + /// 实例 + /// 键值对 + private KeyValuePair Pair(Type type, object instance) + { + return new KeyValuePair(Type2Service(type), instance); + } + + /// + /// 获取重定义的服务所对应的回调 + /// + /// 回调列表 + private IList> GetOnReboundCallbacks(string service) + { + List> result; + return !rebound.TryGetValue(service, out result) ? null : result; + } + + /// + /// 制作一个空的绑定数据 + /// + /// 服务名 + /// 空绑定数据 + private BindData MakeEmptyBindData(string service) + { + return new BindData(this, service, null, false); + } + + /// + /// 解决服务 + /// + /// 服务名或别名 + /// 用户传入的构造参数 + /// 服务实例,如果构造失败那么返回null + /// null或者空字符串 + /// 出现循环依赖 + /// 无法解决服务 + /// 服务实例 + private object Resolve(string service, params object[] userParams) + { + Guard.NotEmptyOrNull(service, "service"); + lock (syncRoot) + { + service = AliasToService(service); + + object instance; + if (instances.TryGetValue(service, out instance)) { - instance = ResolveNonClass(makeServiceBindData, needService); + return instance; } - if (injectAttr != null && injectAttr.Required && instance == null) + if (buildStack.Contains(service)) { - throw new RuntimeException("[" + makeServiceBindData.Service + "] Required [" + info.ParameterType + "] Service."); + throw MakeCircularDependencyException(service); } - if (instance != null && !info.ParameterType.IsInstanceOfType(instance)) + buildStack.Push(service); + userParamsStack.Push(userParams); + try { - throw new RuntimeException("[" + makeServiceBindData.Service + "] Attr inject type must be [" + info.ParameterType + "] , But instance is [" + instance.GetType() + "] Make service is [" + needService + "]."); + var bindData = GetBindFillable(service); + var result = Inject(bindData, Build(bindData, userParams)); + resolved.Add(bindData.Service); + return result; + } + finally + { + userParamsStack.Pop(); + buildStack.Pop(); } - - myParam.Add(instance); } - - return myParam.ToArray(); } /// - /// 解决非类类型 + /// 为对象进行依赖注入 /// - /// 请求注入操作的服务绑定数据 - /// 希望解决的服务名或者别名 - /// 解决结果 - private object ResolveNonClass(BindData makeServiceBindData, string service) + /// 绑定数据 + /// 对象实例 + /// 注入完成的对象 + private object Inject(BindData bindData, object instance) { - return Make(makeServiceBindData.GetContextual(service)); + GuardResolveInstance(instance, bindData.Service); + + AttributeInject(bindData, instance); + + instance = bindData.IsStatic + ? Instance(bindData.Service, instance) + : TriggerOnResolving(bindData, bindData.TriggerResolving(instance)); + + return instance; } /// - /// 解决类类型 + /// 编译服务 /// - /// 请求注入操作的服务绑定数据 - /// 希望解决的服务名或者别名 - /// 解决结果 - private object ResloveClass(BindData makeServiceBindData, string service) + /// 服务绑定数据 + /// 用户传入的构造参数 + /// 服务实例 + private object Build(BindData makeServiceBindData, object[] userParams) { - return Make(makeServiceBindData.GetContextual(service)); + return makeServiceBindData.Concrete != null + ? makeServiceBindData.Concrete(this, userParams) + : CreateInstance(makeServiceBindData, SpeculatedServiceType(makeServiceBindData.Service), + userParams); } /// - /// 获取别名最终对应的服务名 + /// 构造服务实现(准备需要注入的参数) /// - /// 服务名或别名 - /// 最终映射的服务名 - private string AliasToService(string service) + /// 服务绑定数据 + /// 服务类型 + /// 用户传入的构造参数 + /// 服务实例 + private object CreateInstance(Bindable makeServiceBindData, Type makeServiceType, object[] userParams) { - string alias; - return aliases.TryGetValue(service, out alias) ? alias : service; + if (IsUnableType(makeServiceType)) + { + return null; + } + + userParams = GetConstructorsInjectParams(makeServiceBindData, makeServiceType, userParams); + + try + { + // 如果参数不存在那么在反射时不写入参数可以获得更好的性能 + if (userParams == null || userParams.Length <= 0) + { + return Activator.CreateInstance(makeServiceType); + } + return Activator.CreateInstance(makeServiceType, userParams); + } + catch (Exception ex) + { + throw MakeBuildFaildException(makeServiceBindData.Service, makeServiceType, ex); + } } /// - /// 获取服务绑定数据(与GetBind的区别是永远不会为null) + /// 获取服务绑定数据,如果数据为null则填充数据 /// /// 服务名 /// 服务绑定数据 - private BindData GetBindData(string service) + private BindData GetBindFillable(string service) { BindData bindData; - return binds.TryGetValue(service, out bindData) ? bindData : MakeEmptyBindData(service); + return service != null && binds.TryGetValue(service, out bindData) + ? bindData + : MakeEmptyBindData(service); } /// - /// 制作一个空的绑定数据 + /// 从中获取类型的变量 /// - /// 服务名 - /// 空绑定数据 - private BindData MakeEmptyBindData(string service) + /// 用户传入参数 + /// 获取到的参数 + private Params[] GetParamsTypeInUserParams(ref object[] userParams) { - return new BindData(this, service, null, false); + var elements = Arr.Remove(ref userParams, value => value is Params); + var results = new Params[elements.Length]; + for (var i = 0; i < elements.Length; i++) + { + results[i] = (Params)elements[i]; + } + return results; } /// - /// 获取类型映射 + /// 生成一个默认的参数匹配器 /// - /// 服务名 - /// 服务类型 - private Type GetType(string service) + /// 参数表 + /// 匹配器 + private Func MakeParamsMatcher(Params[] tables) { - foreach (var finder in findType) + // 默认匹配器策略将会将参数名和参数表的参数名进行匹配 + // 最先匹配到的有效参数值将作为返回值返回 + return (parameterInfo) => { - var type = finder.Invoke(service); - if (type != null) + foreach (var table in tables) { - return type; + object result; + if (!table.TryGetValue(parameterInfo.Name, out result)) + { + continue; + } + + if (ChangeType(ref result, parameterInfo.ParameterType)) + { + return result; + } } - } - return null; + + return null; + }; } } } \ No newline at end of file diff --git a/src/CatLib.Core/Support/Container/ContainerExtend.cs b/src/CatLib.Core/Support/Container/ContainerExtend.cs index 90cce62..e65639b 100644 --- a/src/CatLib.Core/Support/Container/ContainerExtend.cs +++ b/src/CatLib.Core/Support/Container/ContainerExtend.cs @@ -10,6 +10,7 @@ */ using System; +using System.Collections.Generic; namespace CatLib { @@ -18,6 +19,217 @@ namespace CatLib /// public static class ContainerExtend { + /// + /// 获取服务的绑定数据,如果绑定不存在则返回null + /// + /// 服务名 + /// 服务容器 + /// 服务绑定数据或者null + public static IBindData GetBind(this IContainer container) + { + return container.GetBind(container.Type2Service(typeof(TService))); + } + + /// + /// 是否已经绑定了服务 + /// + /// 服务名 + /// 服务容器 + /// 代表服务是否被绑定 + public static bool HasBind(this IContainer container) + { + return container.HasBind(container.Type2Service(typeof(TService))); + } + + /// + /// 是否已经实例静态化 + /// + /// 服务名 + /// 服务容器 + /// 是否已经静态化 + public static bool HasInstance(this IContainer container) + { + return container.HasInstance(container.Type2Service()); + } + + /// + /// 服务是否已经被解决过 + /// + /// 服务名 + /// 服务容器 + /// 是否已经被解决过 + public static bool IsResolved(this IContainer container) + { + return container.IsResolved(container.Type2Service()); + } + + /// + /// 是否可以生成服务 + /// + /// 服务名 + /// 服务容器 + /// 服务是否可以被构建 + public static bool CanMake(this IContainer container) + { + return container.CanMake(container.Type2Service(typeof(TService))); + } + + /// + /// 服务是否是静态化的,如果服务不存在也将返回false + /// + /// 服务名 + /// 服务容器 + /// 服务是否是静态化的 + public static bool IsStatic(this IContainer container) + { + return container.IsStatic(container.Type2Service(typeof(TService))); + } + + /// + /// 是否是别名 + /// + /// 服务名 + /// 服务容器 + /// 是否是别名 + public static bool IsAlias(this IContainer container) + { + return container.IsAlias(container.Type2Service(typeof(TService))); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名,同时也是服务实现 + /// 服务容器 + /// 服务绑定数据 + public static IBindData Bind(this IContainer container) + { + return container.Bind(container.Type2Service(typeof(TService)), typeof(TService), false); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名 + /// 服务别名 + /// 服务容器 + /// 服务绑定数据 + public static IBindData Bind(this IContainer container) + { + return container.Bind(container.Type2Service(typeof(TService)), typeof(TService), false) + .Alias(container.Type2Service(typeof(TAlias))); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名 + /// 服务容器 + /// 服务实现 + /// 服务绑定数据 + public static IBindData Bind(this IContainer container, Func concrete) + { + Guard.Requires(concrete != null); + return container.Bind(container.Type2Service(typeof(TService)), concrete, false); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务名 + /// 服务容器 + /// 服务实现 + /// 服务绑定数据 + public static IBindData Bind(this IContainer container, Func concrete) + { + return container.Bind(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), false); + } + + /// + /// 常规绑定一个服务 + /// + /// 服务容器 + /// 服务名 + /// 服务实现 + /// 服务绑定数据 + public static IBindData Bind(this IContainer container, string service, + Func concrete) + { + return container.Bind(service, concrete, false); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务别名 + /// 服务容器 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(this IContainer container, out IBindData bindData) + { + if (container.BindIf(container.Type2Service(typeof(TService)), typeof(TService), false, out bindData)) + { + bindData.Alias(container.Type2Service(typeof(TAlias))); + return true; + } + return false; + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名,同时也是服务实现 + /// 服务容器 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(this IContainer container, out IBindData bindData) + { + return container.BindIf(container.Type2Service(typeof(TService)), typeof(TService), false, out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务容器 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(this IContainer container, Func concrete, out IBindData bindData) + { + return container.BindIf(container.Type2Service(typeof(TService)), concrete, false, out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务容器 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(this IContainer container, Func concrete, out IBindData bindData) + { + Guard.Requires(concrete != null); + return container.BindIf(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), false, + out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 + /// + /// 服务容器 + /// 服务名 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool BindIf(this IContainer container, string service, + Func concrete, out IBindData bindData) + { + return container.BindIf(service, concrete, false, out bindData); + } + /// /// 以单例的形式绑定一个服务 /// @@ -50,7 +262,7 @@ public static IBindData Singleton(this IContainer container) /// 服务名,同时也是服务实现 /// 服务容器 /// 服务绑定数据 - public static IBindData Singleton(this IContainer container) where TService : class + public static IBindData Singleton(this IContainer container) { return container.Bind(container.Type2Service(typeof(TService)), typeof(TService), true); } @@ -63,71 +275,340 @@ public static IBindData Singleton(this IContainer container) where TSe /// 服务实现 /// 服务绑定数据 public static IBindData Singleton(this IContainer container, - Func concrete) where TService : class + Func concrete) { return container.Bind(container.Type2Service(typeof(TService)), concrete, true); } /// - /// 常规绑定一个服务 + /// 以单例的形式绑定一个服务 /// /// 服务名 - /// 服务别名 /// 服务容器 + /// 服务实现 /// 服务绑定数据 - public static IBindData Bind(this IContainer container) + public static IBindData Singleton(this IContainer container, + Func concrete) { - return container.Bind(container.Type2Service(typeof(TService)), typeof(TService), false) - .Alias(container.Type2Service(typeof(TAlias))); + Guard.Requires(concrete != null); + return container.Bind(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), true); } /// - /// 常规绑定一个服务 + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务别名 + /// 服务容器 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(this IContainer container, out IBindData bindData) + { + if (container.BindIf(container.Type2Service(typeof(TService)), typeof(TService), true, out bindData)) + { + bindData.Alias(container.Type2Service(typeof(TAlias))); + return true; + } + return false; + } + + /// + /// 如果服务不存在那么则绑定服务 /// /// 服务名,同时也是服务实现 /// 服务容器 - /// 服务绑定数据 - public static IBindData Bind(this IContainer container) where TService : class + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(this IContainer container, out IBindData bindData) { - return container.Bind(container.Type2Service(typeof(TService)), typeof(TService), false); + return container.BindIf(container.Type2Service(typeof(TService)), typeof(TService), true, out bindData); } /// - /// 常规绑定一个服务 + /// 如果服务不存在那么则绑定服务 /// /// 服务名 /// 服务容器 /// 服务实现 - /// 服务绑定数据 - public static IBindData Bind(this IContainer container, Func concrete) - where TService : class + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(this IContainer container, Func concrete, out IBindData bindData) { - return container.Bind(container.Type2Service(typeof(TService)), concrete, false); + return container.BindIf(container.Type2Service(typeof(TService)), concrete, true, out bindData); } /// - /// 常规绑定一个服务 + /// 如果服务不存在那么则绑定服务 + /// + /// 服务名 + /// 服务容器 + /// 服务实现 + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(this IContainer container, Func concrete, out IBindData bindData) + { + Guard.Requires(concrete != null); + return container.BindIf(container.Type2Service(typeof(TService)), (c, p) => concrete.Invoke(), true, + out bindData); + } + + /// + /// 如果服务不存在那么则绑定服务 /// /// 服务容器 /// 服务名 /// 服务实现 - /// 服务绑定数据 - public static IBindData Bind(this IContainer container, string service, - Func concrete) + /// 如果绑定失败则返回历史绑定对象 + /// 是否完成绑定 + public static bool SingletonIf(this IContainer container, string service, + Func concrete, out IBindData bindData) { - return container.Bind(service, concrete, false); + return container.BindIf(service, concrete, true, out bindData); + } + + /// + /// 绑定一个方法到容器 + /// + /// 服务容器 + /// 方法名 + /// 调用目标 + /// 调用方法 + public static IMethodBind BindMethod(this IContainer container, string method, object target, string call = null) + { + Guard.NotEmptyOrNull(method, "method"); + Guard.Requires(target != null); + + return container.BindMethod(method, target, target.GetType().GetMethod(call ?? Str.Method(method))); + } + + /// + /// 绑定一个方法到容器 + /// + /// 服务容器 + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(this IContainer container, string method, Func callback) + { + Guard.Requires(method != null); + Guard.Requires(callback != null); + return container.BindMethod(method, callback.Target, callback.Method); + } + + /// + /// 绑定一个方法到容器 + /// + /// 服务容器 + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(this IContainer container, string method, Func callback) + { + Guard.Requires(method != null); + Guard.Requires(callback != null); + return container.BindMethod(method, callback.Target, callback.Method); + } + + /// + /// 绑定一个方法到容器 + /// + /// 服务容器 + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(this IContainer container, string method, Func callback) + { + Guard.Requires(method != null); + Guard.Requires(callback != null); + return container.BindMethod(method, callback.Target, callback.Method); } /// - /// 构造一个服务,允许传入构造参数 + /// 绑定一个方法到容器 + /// + /// 服务容器 + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(this IContainer container, string method, Func callback) + { + Guard.Requires(method != null); + Guard.Requires(callback != null); + return container.BindMethod(method, callback.Target, callback.Method); + } + + /// + /// 绑定一个方法到容器 + /// + /// 服务容器 + /// 方法名 + /// 调用方法 + public static IMethodBind BindMethod(this IContainer container, string method, Func callback) + { + Guard.Requires(method != null); + Guard.Requires(callback != null); + return container.BindMethod(method, callback.Target, callback.Method); + } + + /// + /// 解除服务绑定 + /// + /// 解除绑定的服务 + /// 服务容器 + public static void Unbind(this IContainer container) + { + container.Unbind(container.Type2Service(typeof(TService))); + } + + /// + /// 静态化一个服务,实例值会经过解决修饰器 /// /// 服务名 /// 服务容器 - /// 构造参数 - /// 服务实例 - public static TService MakeWith(this IContainer container, params object[] param) + /// 实例值 + public static object Instance(this IContainer container, object instance) + { + return container.Instance(container.Type2Service(typeof(TService)), instance); + } + + /// + /// 释放服务 + /// + /// 服务名 + /// 服务容器 + public static void Release(this IContainer container) + { + container.Release(container.Type2Service(typeof(TService))); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 服务容器 + /// 方法 + public static void Call(this IContainer container, Action method) + { + Guard.Requires(method != null); + container.Call(method.Target, method.Method); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 服务容器 + /// 方法 + public static void Call(this IContainer container, Action method) + { + Guard.Requires(method != null); + container.Call(method.Target, method.Method); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 服务容器 + /// 方法 + public static void Call(this IContainer container, Action method) + { + Guard.Requires(method != null); + container.Call(method.Target, method.Method); + } + + /// + /// 以依赖注入的形式调用一个方法 + /// + /// 服务容器 + /// 方法 + public static void Call(this IContainer container, Action method) + { + Guard.Requires(method != null); + container.Call(method.Target, method.Method); + } + + /// + /// 以依赖注入形式调用一个方法 + /// + /// 服务容器 + /// 方法对象 + /// 方法名 + /// 用户传入的参数 + /// 方法返回值 + /// ,null或者空字符串 + public static object Call(this IContainer container, object target, string method, params object[] userParams) + { + Guard.Requires(target != null); + Guard.NotEmptyOrNull(method, "method"); + + var methodInfo = target.GetType().GetMethod(method); + return container.Call(target, methodInfo, userParams); + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 服务容器 + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(this IContainer container, Action method, params object[] userParams) + { + return () => + { + if (method != null) + { + container.Call(method.Target, method.Method, userParams); + } + }; + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 服务容器 + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(this IContainer container, Action method, params object[] userParams) { - return (TService) container.MakeWith(container.Type2Service(typeof(TService)), param); + return () => + { + if (method != null) + { + container.Call(method.Target, method.Method, userParams); + } + }; + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 服务容器 + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(this IContainer container, Action method, params object[] userParams) + { + return () => + { + if (method != null) + { + container.Call(method.Target, method.Method, userParams); + } + }; + } + + /// + /// 包装一个依赖注入形式调用的一个方法 + /// + /// 服务容器 + /// 方法 + /// 用户传入的参数 + /// 包装方法 + public static Action Wrap(this IContainer container, Action method, params object[] userParams) + { + return () => + { + if (method != null) + { + container.Call(method.Target, method.Method, userParams); + } + }; } /// @@ -135,43 +616,118 @@ public static TService MakeWith(this IContainer container, params obje /// /// 服务名 /// 服务容器 + /// 用户提供的参数 /// 服务实例 - public static TService Make(this IContainer container) + public static TService Make(this IContainer container, params object[] userParams) { - return (TService) container.Make(container.Type2Service(typeof(TService))); + return (TService)container.Make(container.Type2Service(typeof(TService)), userParams); } /// /// 构造一个服务 /// - /// 服务实例转换到的类型 /// 服务容器 - /// 服务名 + /// 服务类型 + /// 用户提供的参数 /// 服务实例 - public static TConvert Make(this IContainer container, string service) + public static object Make(this IContainer container, Type type, params object[] userParams) { - return (TConvert) container.Make(service); + var service = container.Type2Service(type); + IBindData binder; + container.BindIf(service, type, false, out binder); + return container.Make(service, userParams); } /// - /// 释放服务 + /// 获取一个回调,当执行回调可以生成指定的服务 /// /// 服务名 /// 服务容器 - public static void Release(this IContainer container) where TService : class + /// 用户传入的参数 + /// 回调方案 + public static Func Factory(this IContainer container, params object[] userParams) { - container.Release(container.Type2Service(typeof(TService))); + return () => (TService)container.Make(container.Type2Service(typeof(TService)), userParams); } /// - /// 静态化一个服务,实例值会经过解决修饰器 + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// + /// 服务容器 + /// 关注的服务名 + /// 当服务发生重定义时调用的目标 + /// 方法名 + public static void Watch(this IContainer container, string service, object target, string method) + { + Guard.Requires(target != null); + Guard.NotEmptyOrNull(method, "method"); + + var methodInfo = target.GetType().GetMethod(method); + container.Watch(service, target, methodInfo); + } + + /// + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 /// /// 服务名 /// 服务容器 - /// 实例值 - public static void Instance(this IContainer container, object instance) where TService : class + /// 当服务发生重定义时调用的目标 + /// 方法名 + public static void Watch(this IContainer container, object target, string method) + { + Guard.Requires(method != null); + container.Watch(container.Type2Service(), target, method); + } + + /// + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// + /// 服务名 + /// 服务容器 + /// 回调 + public static void Watch(this IContainer container, Action method) + { + Guard.Requires(method != null); + container.Watch(container.Type2Service(), method.Target, method.Method); + } + + /// + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// + /// 服务名 + /// 服务容器 + /// 回调 + public static void Watch(this IContainer container, Action method) + { + Guard.Requires(method != null); + container.Watch(container.Type2Service(), method.Target, method.Method); + } + + /// + /// 在回调区间内暂时性的静态化服务实例 + /// + /// 服务容器 + /// 回调区间 + /// 服务名 + /// 实例名 + public static void Flash(this IContainer container, Action callback, string service, object instance) + { + container.Flash(callback, new KeyValuePair(service, instance)); + } + + /// + /// 类型转为服务名 + /// + /// 服务类型 + /// 服务容器 + /// 服务名 + public static string Type2Service(this IContainer container) { - container.Instance(container.Type2Service(typeof(TService)), instance); + return container.Type2Service(typeof(TService)); } } } \ No newline at end of file diff --git a/src/CatLib.Core/Support/Container/GivenData.cs b/src/CatLib.Core/Support/Container/GivenData.cs index af6fe37..b842305 100644 --- a/src/CatLib.Core/Support/Container/GivenData.cs +++ b/src/CatLib.Core/Support/Container/GivenData.cs @@ -14,15 +14,15 @@ namespace CatLib /// /// 绑定关系临时数据,用于支持链式调用 /// - internal sealed class GivenData : IGivenData + internal sealed class GivenData : IGivenData where TReturn : class, IBindable { /// /// 绑定数据 /// - private readonly BindData bindData; - + private readonly Bindable bindable; + /// - /// 服务容器 + /// 父级容器 /// private readonly Container container; @@ -34,12 +34,12 @@ internal sealed class GivenData : IGivenData /// /// 绑定关系临时数据 /// - /// 服务容器 - /// 服务绑定数据 - internal GivenData(Container container, BindData bindData) + /// 依赖注入容器 + /// 可绑定数据 + internal GivenData(Container container, Bindable bindable) { - this.bindData = bindData; this.container = container; + this.bindable = bindable; } /// @@ -47,7 +47,7 @@ internal GivenData(Container container, BindData bindData) /// /// 需求什么服务 /// 绑定关系实例 - internal IGivenData Needs(string needs) + internal IGivenData Needs(string needs) { this.needs = needs; return this; @@ -58,10 +58,11 @@ internal IGivenData Needs(string needs) /// /// 给与的服务名或别名 /// 服务绑定数据 - public IBindData Given(string service) + public TReturn Given(string service) { - Guard.NotEmptyOrNull(service , "service"); - return bindData.AddContextual(needs, service); + Guard.NotEmptyOrNull(service, "service"); + bindable.AddContextual(needs, service); + return bindable as TReturn; } /// @@ -69,7 +70,7 @@ public IBindData Given(string service) /// /// 给与的服务名或别名 /// 服务绑定数据 - public IBindData Given() + public TReturn Given() { return Given(container.Type2Service(typeof(T))); } diff --git a/src/CatLib.Core/Support/Container/Internal/IBindData.cs b/src/CatLib.Core/Support/Container/IBindData.cs similarity index 58% rename from src/CatLib.Core/Support/Container/Internal/IBindData.cs rename to src/CatLib.Core/Support/Container/IBindData.cs index 5ee8608..46aac60 100644 --- a/src/CatLib.Core/Support/Container/Internal/IBindData.cs +++ b/src/CatLib.Core/Support/Container/IBindData.cs @@ -16,13 +16,8 @@ namespace CatLib /// /// 服务绑定数据 /// - public interface IBindData + public interface IBindData : IBindable { - /// - /// 服务名 - /// - string Service { get; } - /// /// 服务实现 /// @@ -33,20 +28,6 @@ public interface IBindData /// bool IsStatic { get; } - /// - /// 当需求某个服务 - /// - /// 服务名 - /// 绑定关系临时数据 - IGivenData Needs(string service); - - /// - /// 当需求某个服务 - /// - /// 服务类型 - /// 绑定关系临时数据 - IGivenData Needs(); - /// /// 为服务设定一个别名 /// @@ -74,10 +55,5 @@ public interface IBindData /// 处理事件 /// 服务绑定数据 IBindData OnRelease(Action action); - - /// - /// 移除绑定服务 , 在解除绑定时如果是静态化物体将会触发释放 - /// - void UnBind(); } } diff --git a/src/CatLib.Core/Support/Container/IBindable.cs b/src/CatLib.Core/Support/Container/IBindable.cs new file mode 100644 index 0000000..51afe23 --- /dev/null +++ b/src/CatLib.Core/Support/Container/IBindable.cs @@ -0,0 +1,50 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +namespace CatLib +{ + /// + /// 被绑定对象 + /// + public interface IBindable + { + /// + /// 当前绑定的名字 + /// + string Service { get; } + + /// + /// 移除绑定 + /// 如果进行的是服务绑定 , 那么在解除绑定时如果是静态化物体将会触发释放 + /// + void Unbind(); + } + + /// + /// 被绑定对象 + /// + public interface IBindable : IBindable + { + /// + /// 当需求某个服务 + /// + /// 服务名 + /// 绑定关系临时数据 + IGivenData Needs(string service); + + /// + /// 当需求某个服务 + /// + /// 服务类型 + /// 绑定关系临时数据 + IGivenData Needs(); + } +} diff --git a/src/CatLib.Core/Support/Container/IContainer.cs b/src/CatLib.Core/Support/Container/IContainer.cs index ffb55d4..6cc7ce6 100644 --- a/src/CatLib.Core/Support/Container/IContainer.cs +++ b/src/CatLib.Core/Support/Container/IContainer.cs @@ -10,12 +10,13 @@ */ using System; +using System.Collections.Generic; using System.Reflection; namespace CatLib { /// - /// 容器接口 + /// 依赖注入容器 /// public interface IContainer { @@ -33,6 +34,27 @@ public interface IContainer /// 返回一个bool值代表服务是否被绑定 bool HasBind(string service); + /// + /// 是否已经实例静态化 + /// + /// 服务名或别名 + /// 是否已经静态化 + bool HasInstance(string service); + + /// + /// 服务是否已经被解决过 + /// + /// 服务名或别名 + /// 是否已经被解决过 + bool IsResolved(string service); + + /// + /// 是否可以生成服务 + /// + /// 服务名或者别名 + /// 是否可以生成服务 + bool CanMake(string service); + /// /// 服务是否是静态化的,如果服务不存在也将返回false /// @@ -40,23 +62,30 @@ public interface IContainer /// 是否是静态化的 bool IsStatic(string service); + /// + /// 是否是别名 + /// + /// 名字 + /// 是否是别名 + bool IsAlias(string name); + /// /// 绑定一个服务 /// /// 服务名 - /// 服务实体 + /// 服务实现 /// 服务是否静态化 /// 服务绑定数据 - IBindData Bind(string service, Func concrete, bool isStatic); + IBindData Bind(string service, Type concrete, bool isStatic); /// /// 绑定一个服务 /// /// 服务名 - /// 服务实现 + /// 服务实体 /// 服务是否静态化 /// 服务绑定数据 - IBindData Bind(string service, Type concrete, bool isStatic); + IBindData Bind(string service, Func concrete, bool isStatic); /// /// 如果服务不存在那么则绑定服务 @@ -64,8 +93,9 @@ public interface IContainer /// 服务名 /// 服务实现 /// 服务是否是静态的 - /// 服务绑定数据 - IBindData BindIf(string service, Func concrete, bool isStatic); + /// 如果绑定失败则返回历史绑定对象 + /// 是否成功绑定 + bool BindIf(string service, Func concrete, bool isStatic, out IBindData bindData); /// /// 如果服务不存在那么则绑定服务 @@ -73,8 +103,35 @@ public interface IContainer /// 服务名 /// 服务实现 /// 服务是否是静态的 - /// 服务绑定数据 - IBindData BindIf(string service, Type concrete, bool isStatic); + /// 如果绑定失败则返回历史绑定对象 + /// 是否成功绑定 + bool BindIf(string service, Type concrete, bool isStatic, out IBindData bindData); + + /// + /// 绑定一个方法到容器 + /// + /// 方法名 + /// 调用目标 + /// 调用方法 + /// 方法绑定数据 + IMethodBind BindMethod(string method, object target, MethodInfo call); + + /// + /// 解除绑定的方法 + /// + /// + /// 解除目标 + /// 如果为字符串则作为调用方法名 + /// 如果为IMethodBind则作为指定方法 + /// 如果为其他对象则作为调用目标做全体解除 + /// + void UnbindMethod(object target); + + /// + /// 解除绑定服务 + /// + /// 服务名或者别名 + void Unbind(string service); /// /// 为一个及以上的服务定义一个标记 @@ -95,7 +152,7 @@ public interface IContainer /// /// 服务名或者别名 /// 服务实例 - void Instance(string service, object instance); + object Instance(string service, object instance); /// /// 释放某个静态化实例 @@ -104,48 +161,34 @@ public interface IContainer void Release(string service); /// - /// 清空容器的所有实例,绑定,别名,标签,解决器 + /// 清空容器的所有实例,绑定,别名,标签,解决器,方法容器 /// void Flush(); /// - /// 当静态服务被释放时 + /// 调用一个已经被绑定的方法 /// - /// 处理释放时的回调 - IContainer OnRelease(Action action); - - /// - /// 以依赖注入形式调用一个方法 - /// - /// 方法对象 /// 方法名 - /// 方法参数 - /// 方法返回值 - object Call(object instance, string method, params object[] param); + /// 用户提供的参数 + /// 调用结果 + object Invoke(string method, params object[] userParams); /// /// 以依赖注入形式调用一个方法 /// /// 方法对象 /// 方法信息 - /// 方法参数 + /// 用户传入的参数 /// 方法返回值 - object Call(object instance, MethodInfo methodInfo, params object[] param); - - /// - /// 构造服务,允许传入参数来决定构造函数的值 - /// - /// 服务名或别名 - /// 构造参数 - /// 服务实例,如果构造失败那么返回null - object MakeWith(string service, params object[] param); + object Call(object instance, MethodInfo methodInfo, params object[] userParams); /// /// 构造服务 /// /// 服务名或别名 + /// 用户传入的参数 /// 服务实例,如果构造失败那么返回null - object Make(string service); + object Make(string service, params object[] userParams); /// /// 构造服务 @@ -155,7 +198,15 @@ public interface IContainer object this[string service] { get; } /// - /// 为服务设定一个别名 + /// 获取一个回调,当执行回调可以生成指定的服务 + /// + /// 服务名或别名 + /// 用户传入的参数 + /// 回调方案 + Func Factory(string service, params object[] userParams); + + /// + /// 以全局的方式为服务设定一个别名 /// /// 别名 /// 映射到的服务名 @@ -169,6 +220,12 @@ public interface IContainer /// 当前容器实例 IContainer OnResolving(Func func); + /// + /// 当静态服务被释放时 + /// + /// 处理释放时的回调 + IContainer OnRelease(Action action); + /// /// 当查找类型无法找到时会尝试去调用开发者提供的查找类型函数 /// @@ -177,6 +234,31 @@ public interface IContainer /// 当前容器实例 IContainer OnFindType(Func func, int priority = int.MaxValue); + /// + /// 当一个已经被解决的服务,发生重定义时触发 + /// + /// 服务名 + /// 回调 + /// 服务容器 + IContainer OnRebound(string service, Action callback); + + /// + /// 关注指定的服务,当服务触发重定义时调用指定对象的指定方法 + /// 调用是以依赖注入的形式进行的 + /// 服务的新建(第一次解决服务)操作并不会触发重定义 + /// + /// 关注的服务名 + /// 当服务发生重定义时调用的目标 + /// 方法信息 + void Watch(string service, object target, MethodInfo methodInfo); + + /// + /// 在回调区间内暂时性的静态化服务实例 + /// + /// 回调区间 + /// 服务映射 + void Flash(Action callback, params KeyValuePair[] services); + /// /// 类型转为服务名 /// diff --git a/src/CatLib.Core/Support/Container/Internal/IGivenData.cs b/src/CatLib.Core/Support/Container/IGivenData.cs similarity index 87% rename from src/CatLib.Core/Support/Container/Internal/IGivenData.cs rename to src/CatLib.Core/Support/Container/IGivenData.cs index 5a32824..ecf6f2f 100644 --- a/src/CatLib.Core/Support/Container/Internal/IGivenData.cs +++ b/src/CatLib.Core/Support/Container/IGivenData.cs @@ -14,20 +14,20 @@ namespace CatLib /// /// 绑定关系临时数据,用于支持链式调用 /// - public interface IGivenData + public interface IGivenData { /// /// 给与什么服务 /// /// 给与的服务名或别名 /// 服务绑定数据 - IBindData Given(string service); + TReturn Given(string service); /// /// 给与什么服务 /// /// 给与的服务名或别名 /// 服务绑定数据 - IBindData Given(); + TReturn Given(); } } \ No newline at end of file diff --git a/src/CatLib.Core/Support/Container/IMethodBind.cs b/src/CatLib.Core/Support/Container/IMethodBind.cs new file mode 100644 index 0000000..c72a4f7 --- /dev/null +++ b/src/CatLib.Core/Support/Container/IMethodBind.cs @@ -0,0 +1,20 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +namespace CatLib +{ + /// + /// 方法绑定数据 + /// + public interface IMethodBind : IBindable + { + } +} diff --git a/src/CatLib.Core/Support/Container/IVariant.cs b/src/CatLib.Core/Support/Container/IVariant.cs new file mode 100644 index 0000000..f3c1dde --- /dev/null +++ b/src/CatLib.Core/Support/Container/IVariant.cs @@ -0,0 +1,21 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +namespace CatLib +{ + /// + /// 可转变的 + /// 实现该接口的类,允许依赖注入容器将用户传入的基本类型(包含string)转变为目标类 + /// + public interface IVariant + { + } +} diff --git a/src/CatLib.Core/Support/Container/InjectAttribute.cs b/src/CatLib.Core/Support/Container/InjectAttribute.cs index 88cb3a8..a62e0e4 100644 --- a/src/CatLib.Core/Support/Container/InjectAttribute.cs +++ b/src/CatLib.Core/Support/Container/InjectAttribute.cs @@ -26,12 +26,6 @@ public class InjectAttribute : Attribute /// public string Alias { get; private set; } - /// - /// 是否是必须的 - /// 如果约束为必须当依赖注入失败时则会引发一个异常 - /// - public bool Required { get; set; } - /// /// 声明注入 /// diff --git a/src/CatLib.Core/Support/Container/MethodBind.cs b/src/CatLib.Core/Support/Container/MethodBind.cs new file mode 100644 index 0000000..3ecceec --- /dev/null +++ b/src/CatLib.Core/Support/Container/MethodBind.cs @@ -0,0 +1,66 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System.Reflection; + +namespace CatLib +{ + /// + /// 方法绑定数据 + /// + internal sealed class MethodBind : Bindable , IMethodBind + { + /// + /// 方法信息 + /// + public MethodInfo MethodInfo { get; private set; } + + /// + /// 调用目标 + /// + public object Target { get; private set; } + + /// + /// 参数表 + /// + public ParameterInfo[] ParameterInfos { get; private set; } + + /// + /// 方法容器 + /// + private readonly MethodContainer methodContainer; + + /// + /// 构建一个绑定数据 + /// + /// 方法容器 + /// 依赖注入容器 + /// 服务名 + /// 调用目标 + /// 调用方法 + public MethodBind(MethodContainer methodContainer, Container container, string service, object target, MethodInfo call) + :base(container, service) + { + this.methodContainer = methodContainer; + Target = target; + MethodInfo = call; + ParameterInfos = call.GetParameters(); + } + + /// + /// 解除绑定 + /// + protected override void ReleaseBind() + { + methodContainer.Unbind(this); + } + } +} diff --git a/src/CatLib.Core/Support/Container/MethodContainer.cs b/src/CatLib.Core/Support/Container/MethodContainer.cs new file mode 100644 index 0000000..093a413 --- /dev/null +++ b/src/CatLib.Core/Support/Container/MethodContainer.cs @@ -0,0 +1,235 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace CatLib +{ + /// + /// 方法容器 + /// + internal sealed class MethodContainer + { + /// + /// 调用方法目标 映射到 方法名字 + /// + private readonly Dictionary> targetToMethodsMappings; + + /// + /// 绑定数据 + /// + private readonly Dictionary methodMappings; + + /// + /// 依赖注入容器 + /// + private readonly Container container; + + /// + /// 依赖解决器 + /// + private readonly Func dependenciesResolved; + + /// + /// 同步锁 + /// + private readonly object syncRoot; + + /// + /// 构建一个新的方法容器 + /// + /// + /// 依赖解决器 + internal MethodContainer(Container container, Func dependenciesResolved) + { + this.container = container; + targetToMethodsMappings = new Dictionary>(); + methodMappings = new Dictionary(); + this.dependenciesResolved = dependenciesResolved; + syncRoot = new object(); + } + + /// + /// 绑定一个方法 + /// + /// 通过这个名字可以调用方法 + /// 方法调用目标 + /// 在方法调用目标中被调用的方法 + /// + public IMethodBind Bind(string method, object target, MethodInfo methodInfo) + { + Guard.NotEmptyOrNull(method, "method"); + Guard.Requires(methodInfo != null); + + if (!methodInfo.IsStatic) + { + Guard.Requires(target != null); + } + + lock (syncRoot) + { + if (methodMappings.ContainsKey(method)) + { + throw new RuntimeException("Method [" + method + "] is already bind"); + } + + var methodBind = new MethodBind(this, container, method, target, methodInfo); + methodMappings[method] = methodBind; + + if (target == null) + { + return methodBind; + } + + List targetMappings; + if (!targetToMethodsMappings.TryGetValue(target, out targetMappings)) + { + targetToMethodsMappings[target] = targetMappings = new List(); + } + + targetMappings.Add(method); + return methodBind; + } + } + + /// + /// 调用方法 + /// + /// 方法名 + /// 用户传入的参数 + /// 方法调用结果 + public object Invoke(string method, params object[] userParams) + { + Guard.NotEmptyOrNull(method, "method"); + + lock (syncRoot) + { + MethodBind methodBind; + if (!methodMappings.TryGetValue(method, out methodBind)) + { + throw MakeMethodNotFoundException(method); + } + + var injectParams = dependenciesResolved(methodBind, methodBind.ParameterInfos, userParams) ?? + new object[] { }; + return methodBind.MethodInfo.Invoke(methodBind.Target, injectParams); + } + } + + /// + /// 解除绑定 + /// + /// + /// 解除目标 + /// 如果为字符串则作为调用方法名 + /// 如果为IMethodBind则作为指定方法 + /// 如果为其他对象则作为调用目标做全体解除 + /// + public void Unbind(object target) + { + Guard.Requires(target != null); + + lock (syncRoot) + { + var methodBind = target as MethodBind; + + if (methodBind != null) + { + methodBind.Unbind(); + return; + } + + if (target is string) + { + if (!methodMappings.TryGetValue(target.ToString(), out methodBind)) + { + return; + } + methodBind.Unbind(); + return; + } + + UnbindWithObject(target); + } + } + + /// + /// 解除绑定 + /// + /// 方法绑定 + internal void Unbind(MethodBind methodBind) + { + lock (syncRoot) + { + methodMappings.Remove(methodBind.Service); + + if (methodBind.Target == null) + { + return; + } + + List methods; + if (!targetToMethodsMappings.TryGetValue(methodBind.Target, out methods)) + { + return; + } + + methods.Remove(methodBind.Service); + + if (methods.Count <= 0) + { + targetToMethodsMappings.Remove(methodBind.Target); + } + } + } + + /// + /// 根据对象绑定移除为该对象绑定的所有方法 + /// + /// 对象信息 + private void UnbindWithObject(object target) + { + List methods; + if (!targetToMethodsMappings.TryGetValue(target, out methods)) + { + return; + } + + foreach (var method in methods.ToArray()) + { + Unbind(method); + } + } + + /// + /// 清空容器的所有实例,绑定,别名,标签,解决器 + /// + public void Flush() + { + lock (syncRoot) + { + targetToMethodsMappings.Clear(); + methodMappings.Clear(); + } + } + + /// + /// 生成一个方法没有找到异常 + /// + /// + private RuntimeException MakeMethodNotFoundException(string method) + { + return new RuntimeException("Method [" + method + "] is not found."); + } + } +} diff --git a/src/CatLib.Core/Support/Container/Params.cs b/src/CatLib.Core/Support/Container/Params.cs new file mode 100644 index 0000000..f182d79 --- /dev/null +++ b/src/CatLib.Core/Support/Container/Params.cs @@ -0,0 +1,93 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System.Collections.Generic; + +namespace CatLib +{ + /// + /// 参数名注入表 + /// + [ExcludeFromCodeCoverage] + public sealed class Params : IEnumerable> + { + /// + /// 参数表 + /// + private readonly Dictionary table = new Dictionary(); + + /// + /// 迭代器 + /// + /// 迭代器 + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return table.GetEnumerator(); + } + + /// + /// + /// + /// + IEnumerator> IEnumerable>.GetEnumerator() + { + return table.GetEnumerator(); + } + + /// + /// 获取或者设定一个参数 + /// + /// 参数名 + /// 参数值 + public object this[string key] + { + get + { + return table[key]; + } + set + { + table[key] = value; + } + } + + /// + /// 增加一个参数 + /// + /// 参数名 + /// 参数值 + public void Add(string key, object value) + { + table.Add(key, value); + } + + /// + /// 移除参数 + /// + /// 参数名 + /// + public bool Remove(string key) + { + return table.Remove(key); + } + + /// + /// 获取一个参数 + /// + /// 参数名 + /// 参数值 + /// 是否成功获取 + public bool TryGetValue(string key, out object value) + { + return table.TryGetValue(key, out value); + } + } +} diff --git a/src/CatLib.Core/Support/Container/UnresolvableException.cs b/src/CatLib.Core/Support/Container/UnresolvableException.cs new file mode 100644 index 0000000..2d1bb5e --- /dev/null +++ b/src/CatLib.Core/Support/Container/UnresolvableException.cs @@ -0,0 +1,47 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; + +namespace CatLib +{ + /// + /// 未能解决异常 + /// + [ExcludeFromCodeCoverage] + public class UnresolvableException : RuntimeException + { + /// + /// 未能解决异常 + /// + public UnresolvableException() : base() + { + + } + + /// + /// 未能解决异常 + /// + /// 异常消息 + public UnresolvableException(string message) : base(message) + { + } + + /// + /// 未能解决异常 + /// + /// 异常消息 + /// 内部异常 + public UnresolvableException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/CatLib.Core/Support/Dispatcher/Dispatcher.cs b/src/CatLib.Core/Support/Dispatcher/Dispatcher.cs deleted file mode 100644 index 88f4913..0000000 --- a/src/CatLib.Core/Support/Dispatcher/Dispatcher.cs +++ /dev/null @@ -1,274 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace CatLib -{ - /// - /// 事件调度器 - /// - public sealed class Dispatcher : IDispatcher - { - /// - /// 事件句柄 - /// - private readonly Dictionary> handlers; - - /// - /// 通配符事件句柄 - /// - private readonly Dictionary> wildcardHandlers; - - /// - /// 同步锁 - /// - private readonly object syncRoot; - - /// - /// 跳出标记 - /// - private readonly object breakFlag = false; - - /// - /// 调度器 - /// - public Dispatcher() - { - handlers = new Dictionary>(); - wildcardHandlers = new Dictionary>(); - syncRoot = new object(); - } - - /// - /// 触发一个事件,并获取事件的返回结果 - /// - /// 事件名称 - /// 载荷 - /// 事件结果 - public object[] Trigger(string eventName, object payload = null) - { - return Dispatch(eventName, payload) as object[]; - } - - /// - /// 触发一个事件,遇到第一个事件存在处理结果后终止,并获取事件的返回结果 - /// - /// 事件名 - /// 载荷 - /// 事件结果 - public object TriggerHalt(string eventName, object payload = null) - { - return Dispatch(eventName, payload, true); - } - - /// - /// 调度事件 - /// - /// 事件名 - /// 载荷 - /// 遇到第一个事件存在处理结果后终止 - /// 处理结果 - private object Dispatch(string eventName, object payload = null, bool halt = false) - { - Guard.Requires(eventName != null); - eventName = Normalize(eventName); - - lock (syncRoot) - { - var listeners = GetListeners(eventName); - var outputs = new List(listeners.Count); - var triggerListener = new List(listeners.Count); - - foreach (var listener in listeners) - { - var result = listener.Trigger(payload); - triggerListener.Add(listener); - - if (halt && result != null) - { - outputs.Add(result); - break; - } - - if (result != null && result.Equals(breakFlag)) - { - break; - } - - outputs.Add(result); - } - - foreach (var listener in triggerListener) - { - if (!listener.IsLife) - { - listener.Off(); - } - } - - return halt ? outputs.Count <= 0 ? null : outputs[Math.Max(0, outputs.Count - 1)] : outputs.ToArray(); - } - } - - /// - /// 注册一个事件 - /// - /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - public IEventHandler On(string eventName, Action handler, int life = 0) - { - Guard.Requires(handler != null); - return Listen(eventName, (payload) => - { - handler.Invoke(payload); - return null; - }, life); - } - - /// - /// 注册一个事件 - /// - /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - public IEventHandler Listen(string eventName, Func handler, int life = 0) - { - Guard.Requires(eventName != null); - Guard.Requires(handler != null); - - eventName = Normalize(eventName); - - var wildcard = eventName.IndexOf("*") != -1; - var eventHandler = new EventHandler(this, wildcard ? Str.AsteriskWildcard(eventName) : eventName, handler, life); - - lock (syncRoot) - { - if (wildcard) - { - SetWildcardListener(eventHandler); - } - else - { - List handlers; - if (!this.handlers.TryGetValue(eventName, out handlers)) - { - this.handlers[eventName] = handlers = new List(); - } - handlers.Add(eventHandler); - } - - return eventHandler; - } - } - - /// - /// 移除一个事件 - /// - /// 事件句柄 - internal void Off(EventHandler handler) - { - lock (syncRoot) - { - List result; - if (handlers.TryGetValue(handler.EventName, out result)) - { - result.Remove(handler); - if (result.Count <= 0) - { - handlers.Remove(handler.EventName); - } - } - - Regex wildcardkey = null; - foreach (var element in wildcardHandlers) - { - if (element.Key.ToString() == handler.EventName) - { - element.Value.Remove(handler); - wildcardkey = element.Key; - result = element.Value; - break; - } - } - if (wildcardkey != null && result.Count <= 0) - { - wildcardHandlers.Remove(wildcardkey); - } - } - } - - /// - /// 设定通配符监听 - /// - /// 监听句柄 - private void SetWildcardListener(EventHandler handler) - { - List handlers = null; - foreach (var element in wildcardHandlers) - { - if (element.Key.ToString() == handler.EventName) - { - handlers = element.Value; - break; - } - } - - if (handlers == null) - { - wildcardHandlers[new Regex(handler.EventName)] = handlers = new List(); - } - - handlers.Add(handler); - } - - /// - /// 获取指定事件的事件句柄列表 - /// - /// 事件名 - /// 句柄列表 - private IList GetListeners(string eventName) - { - var outputs = new List(); - - List result; - if (handlers.TryGetValue(eventName, out result)) - { - outputs.AddRange(result); - } - - foreach (var element in wildcardHandlers) - { - if (element.Key.IsMatch(eventName)) - { - outputs.AddRange(element.Value); - } - } - - return outputs; - } - - /// - /// 标准化字符串 - /// - /// 输入 - /// 输出 - private string Normalize(string input) - { - return input.ToLower(); - } - } -} diff --git a/src/CatLib.Core/Support/Dispatcher/EventHandler.cs b/src/CatLib.Core/Support/Dispatcher/EventHandler.cs deleted file mode 100644 index 44f9378..0000000 --- a/src/CatLib.Core/Support/Dispatcher/EventHandler.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using System; - -namespace CatLib -{ - /// - /// 事件句柄 - /// - internal sealed class EventHandler : IEventHandler - { - /// - /// 剩余的调用次数 - /// - public int Life { get; private set; } - - /// - /// 事件是否是有效的 - /// - public bool IsLife { get; private set; } - - /// - /// 事件名 - /// - internal string EventName { get; private set; } - - /// - /// 调度器 - /// - private readonly Dispatcher dispatcher; - - /// - /// 事件句柄 - /// - private readonly Func handler; - - /// - /// 是否取消事件 - /// - private bool isCancel; - - /// - /// 调用计数 - /// - private int count; - - /// - /// 创建一个事件句柄 - /// - /// 调度器 - /// 事件名 - /// 事件句柄 - /// 生命次数 - internal EventHandler(Dispatcher dispatcher, string eventName, Func handler, int life) - { - this.dispatcher = dispatcher; - this.handler = handler; - - EventName = eventName; - Life = Math.Max(0, life); - IsLife = true; - - isCancel = false; - count = 0; - } - - /// - /// 撤销事件监听 - /// - /// 是否撤销成功 - public void Off() - { - if (count > 0 || isCancel) - { - return; - } - - dispatcher.Off(this); - isCancel = true; - } - - /// - /// 激活事件 - /// - /// 载荷 - internal object Trigger(object payload) - { - if (!IsLife) - { - return null; - } - - if (Life > 0) - { - if (--Life <= 0) - { - IsLife = false; - } - } - - count++; - - var result = handler.Invoke(payload); - - count--; - - Guard.Requires(count >= 0); - - return result; - } - } -} \ No newline at end of file diff --git a/src/CatLib.Core/Support/Dispatcher/IDispatcher.cs b/src/CatLib.Core/Support/Dispatcher/IDispatcher.cs deleted file mode 100644 index 749b8cd..0000000 --- a/src/CatLib.Core/Support/Dispatcher/IDispatcher.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using System; - -namespace CatLib -{ - /// - /// 调度器 - /// - public interface IDispatcher - { - /// - /// 触发一个事件,并获取事件的返回结果 - /// - /// 事件名称 - /// 载荷 - /// 事件结果 - object[] Trigger(string eventName, object payload = null); - - /// - /// 触发一个事件,遇到第一个事件存在处理结果后终止,并获取事件的返回结果 - /// - /// 事件名 - /// 载荷 - /// 事件结果 - object TriggerHalt(string eventName, object payload = null); - - /// - /// 注册一个事件 - /// - /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - IEventHandler On(string eventName, Action handler, int life = 0); - - /// - /// 注册一个事件 - /// - /// 事件名称 - /// 事件句柄 - /// 在几次后事件会被自动释放 - /// 事件句柄 - IEventHandler Listen(string eventName, Func handler, int life = 0); - } -} diff --git a/src/CatLib.Core/Support/Dispatcher/IEventHandler.cs b/src/CatLib.Core/Support/Dispatcher/IEventHandler.cs deleted file mode 100644 index b305777..0000000 --- a/src/CatLib.Core/Support/Dispatcher/IEventHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -namespace CatLib -{ - /// - /// 事件句柄 - /// - public interface IEventHandler - { - /// - /// 取消注册的事件 - /// - void Off(); - - /// - /// 剩余的调用次数,当为0时事件会被释放 - /// - int Life { get; } - - /// - /// 事件是否是有效的 - /// - bool IsLife { get; } - } -} \ No newline at end of file diff --git a/src/CatLib.Core/Support/Events/Dispatcher.cs b/src/CatLib.Core/Support/Events/Dispatcher.cs new file mode 100644 index 0000000..d88b3c8 --- /dev/null +++ b/src/CatLib.Core/Support/Events/Dispatcher.cs @@ -0,0 +1,484 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace CatLib +{ + /// + /// 事件调度器 + /// + public class Dispatcher : IDispatcher + { + /// + /// 调用方法目标 映射到 事件句柄 + /// + private readonly Dictionary> targetMapping; + + /// + /// 普通事件列表 + /// + private readonly Dictionary> listeners; + + /// + /// 通配符事件列表 + /// + private readonly Dictionary>> wildcardListeners; + + /// + /// 依赖注入容器 + /// + private readonly IContainer container; + + /// + /// 依赖注入容器 + /// + protected IContainer Container + { + get { return container; } + } + + /// + /// 同步锁 + /// + private readonly object syncRoot; + + /// + /// 跳出标记 + /// + protected virtual object BreakFlag + { + get { return false; } + } + + /// + /// 构建一个事件调度器 + /// + /// 依赖注入容器 + public Dispatcher(IContainer container) + { + Guard.Requires(container != null); + + this.container = container; + syncRoot = new object(); + targetMapping = new Dictionary>(); + listeners = new Dictionary>(); + wildcardListeners = new Dictionary>>(); + } + + /// + /// 判断给定事件是否存在事件监听器 + /// + /// 事件名 + /// + /// 严格模式 + /// 启用严格模式则不使用正则来进行匹配事件监听器 + /// + /// 是否存在事件监听器 + public bool HasListeners(string eventName, bool strict = false) + { + eventName = FormatEventName(eventName); + lock (syncRoot) + { + if (listeners.ContainsKey(eventName) + || wildcardListeners.ContainsKey(eventName)) + { + return true; + } + + if (strict) + { + return false; + } + + foreach (var element in wildcardListeners) + { + if (element.Value.Key.IsMatch(eventName)) + { + return true; + } + } + + return false; + } + } + + /// + /// 触发一个事件,并获取事件监听器的返回结果 + /// + /// 事件名称 + /// 载荷 + /// 事件结果 + public object[] Trigger(string eventName, params object[] payloads) + { + return Dispatch(false, eventName, payloads) as object[]; + } + + /// + /// 触发一个事件,并获取事件监听器的返回结果 + /// + /// 事件名称 + /// 载荷 + /// 事件结果 + public object TriggerHalt(string eventName, params object[] payloads) + { + return Dispatch(true, eventName, payloads); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 调用目标 + /// 调用方法 + /// 事件对象 + public IEvent On(string eventName, object target, MethodInfo method) + { + Guard.NotEmptyOrNull(eventName, "eventName"); + Guard.Requires(target != null); + Guard.Requires(method != null); + + lock (syncRoot) + { + eventName = FormatEventName(eventName); + + var result = IsWildcard(eventName) + ? SetupWildcardListen(eventName, target, method) + : SetupListen(eventName, target, method); + + List listener; + if (!targetMapping.TryGetValue(target, out listener)) + { + targetMapping[target] = listener = new List(); + } + listener.Add(result); + + return result; + } + } + + /// + /// 解除注册的事件监听器 + /// + /// + /// 事件解除目标 + /// 如果传入的是字符串(string)将会解除对应事件名的所有事件 + /// 如果传入的是事件对象(IEvent)那么解除对应事件 + /// 如果传入的是其他实例(object)会解除该实例下的所有事件 + /// + public void Off(object target) + { + Guard.Requires(target != null); + + lock (syncRoot) + { + var baseEvent = target as IEvent; + if (baseEvent != null) + { + Forget(baseEvent); + return; + } + + if (target is string) + { + var eventName = FormatEventName(target.ToString()); + if (IsWildcard(eventName)) + { + DismissWildcardEventName(eventName); + } + else + { + DismissEventName(eventName); + } + return; + } + + DismissTargetObject(target); + } + } + + /// + /// 生成事件 + /// + /// 事件名 + /// 目标对象 + /// 调用方法 + /// 是否是通配符事件 + protected virtual IEvent MakeEvent(string eventName, object target, MethodInfo method, bool isWildcard = false) + { + return new Event(eventName, target, method, MakeListener(target, method, isWildcard)); + } + + /// + /// 创建事件监听器 + /// + /// 调用目标 + /// 调用方法 + /// 是否是通配符方法 + /// 事件监听器 + protected virtual Func MakeListener(object target, MethodInfo method, bool isWildcard = false) + { + return (eventName, payloads) => Container.Call(target, method, isWildcard + ? Arr.Merge(new object[] { eventName }, payloads) + : payloads); + } + + /// + /// 格式化事件名 + /// + /// 事件名 + /// 格式化后的事件名 + protected virtual string FormatEventName(string eventName) + { + return eventName; + } + + /// + /// 调度事件 + /// + /// 遇到第一个事件存在处理结果后终止 + /// 事件名 + /// 载荷 + /// 处理结果 + private object Dispatch(bool halt, string eventName, params object[] payload) + { + Guard.Requires(eventName != null); + eventName = FormatEventName(eventName); + + lock (syncRoot) + { + var outputs = new List(listeners.Count); + + foreach (var listener in GetListeners(eventName)) + { + var response = listener.Call(eventName, payload); + + // 如果启用了事件暂停,且得到的有效的响应那么我们终止事件调用 + if (halt && response != null) + { + return response; + } + + // 如果响应内容和终止标记相同那么我们终止事件调用 + if (response != null && response.Equals(BreakFlag)) + { + break; + } + + outputs.Add(response); + } + + return halt ? null : outputs.ToArray(); + } + } + + /// + /// 获取指定事件的事件列表 + /// + /// 事件名 + /// 事件列表 + private IEnumerable GetListeners(string eventName) + { + var outputs = new List(); + + List result; + if (listeners.TryGetValue(eventName, out result)) + { + outputs.AddRange(result); + } + + foreach (var element in wildcardListeners) + { + if (element.Value.Key.IsMatch(eventName)) + { + outputs.AddRange(element.Value.Value); + } + } + + return outputs; + } + + /// + /// 根据普通事件解除相关事件 + /// + /// 事件名 + private void DismissEventName(string eventName) + { + List events; + if (!listeners.TryGetValue(eventName, out events)) + { + return; + } + + foreach (var element in events.ToArray()) + { + Forget(element); + } + } + + /// + /// 根据通配符事件解除相关事件 + /// + /// 事件名 + private void DismissWildcardEventName(string eventName) + { + KeyValuePair> events; + if (!wildcardListeners.TryGetValue(eventName, out events)) + { + return; + } + + foreach (var element in events.Value.ToArray()) + { + Forget(element); + } + } + + /// + /// 根据Object解除事件 + /// + /// 事件解除目标 + private void DismissTargetObject(object target) + { + List events; + if (!targetMapping.TryGetValue(target, out events)) + { + return; + } + + foreach (var element in events.ToArray()) + { + Forget(element); + } + } + + /// + /// 从事件调度器中移除指定的事件监听器 + /// + /// 事件监听器 + private void Forget(IEvent target) + { + lock (syncRoot) + { + List events; + if (targetMapping.TryGetValue(target.Target, out events)) + { + events.Remove(target); + if (events.Count <= 0) + { + targetMapping.Remove(target.Target); + } + } + + if (IsWildcard(target.EventName)) + { + ForgetWildcardListen(target); + } + else + { + ForgetListen(target); + } + } + } + + /// + /// 销毁普通事件 + /// + /// 事件对象 + private void ForgetListen(IEvent target) + { + List events; + if (!listeners.TryGetValue(target.EventName, out events)) + { + return; + } + + events.Remove(target); + if (events.Count <= 0) + { + listeners.Remove(target.EventName); + } + } + + /// + /// 销毁通配符事件 + /// + /// 事件对象 + private void ForgetWildcardListen(IEvent target) + { + KeyValuePair> wildcardEvents; + if (!wildcardListeners.TryGetValue(target.EventName, out wildcardEvents)) + { + return; + } + + wildcardEvents.Value.Remove(target); + if (wildcardEvents.Value.Count <= 0) + { + wildcardListeners.Remove(target.EventName); + } + } + + /// + /// 设定普通事件 + /// + /// 事件名 + /// 事件调用目标 + /// 事件调用方法 + /// 监听事件 + private IEvent SetupListen(string eventName, object target, MethodInfo method) + { + List listener; + if (!listeners.TryGetValue(eventName, out listener)) + { + listeners[eventName] = listener = new List(); + } + + var output = MakeEvent(eventName, target, method); + listener.Add(output); + return output; + } + + /// + /// 设定通配符事件 + /// + /// 事件名 + /// 事件调用目标 + /// 事件调用方法 + /// 监听事件 + private IEvent SetupWildcardListen(string eventName, object target, MethodInfo method) + { + KeyValuePair> listener; + if (!wildcardListeners.TryGetValue(eventName, out listener)) + { + wildcardListeners[eventName] = listener = + new KeyValuePair>(new Regex(Str.AsteriskWildcard(eventName)), new List()); + } + + var output = MakeEvent(eventName, target, method, true); + listener.Value.Add(output); + return output; + } + + /// + /// 是否是通配符事件 + /// + /// 事件名 + /// 是否是通配符事件 + private bool IsWildcard(string eventName) + { + return eventName.IndexOf('*') != -1; + } + } +} diff --git a/src/CatLib.Core/Support/Events/DispatcherExtend.cs b/src/CatLib.Core/Support/Events/DispatcherExtend.cs new file mode 100644 index 0000000..ba8ec86 --- /dev/null +++ b/src/CatLib.Core/Support/Events/DispatcherExtend.cs @@ -0,0 +1,168 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; + +namespace CatLib +{ + /// + /// 事件调度器扩展方法 + /// + public static class DispatcherExtend + { + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件调用目标 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(this IDispatcher dispatcher, string eventName, object target, string method = null) + { + Guard.NotEmptyOrNull(eventName, "eventName"); + Guard.Requires(method != string.Empty); + Guard.Requires(target != null); + + return dispatcher.On(eventName, target, target.GetType().GetMethod(method ?? Str.Method(eventName))); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(this IDispatcher dispatcher, string eventName, Action method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(this IDispatcher dispatcher, string eventName, Action method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(this IDispatcher dispatcher, string eventName, Action method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(this IDispatcher dispatcher, string eventName, Action method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent On(this IDispatcher dispatcher, string eventName, Action method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(this IDispatcher dispatcher, string eventName, Func method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(this IDispatcher dispatcher, string eventName, Func method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(this IDispatcher dispatcher, string eventName, Func method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(this IDispatcher dispatcher, string eventName, Func method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + + /// + /// 注册一个事件监听器 + /// + /// 事件调度器 + /// 事件名称 + /// 事件处理方法 + /// 事件对象 + public static IEvent Listen(this IDispatcher dispatcher, string eventName, Func method) + { + Guard.Requires(method != null); + return dispatcher.On(eventName, method.Target, method.Method); + } + } +} diff --git a/src/CatLib.Core/Support/Events/Event.cs b/src/CatLib.Core/Support/Events/Event.cs new file mode 100644 index 0000000..21227ea --- /dev/null +++ b/src/CatLib.Core/Support/Events/Event.cs @@ -0,0 +1,67 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Reflection; + +namespace CatLib +{ + /// + /// 事件对象 + /// + internal class Event : IEvent + { + /// + /// 事件名 + /// + public string EventName { get; private set; } + + /// + /// 事件目标 + /// + public object Target { get; private set; } + + /// + /// 方法信息 + /// + public MethodInfo Method { get; private set; } + + /// + /// 事件调用 + /// + private readonly Func transfer; + + /// + /// 创建一个事件对象 + /// + /// 事件名 + /// 调用方法目标 + /// 目标方法 + /// 调用方法 + public Event(string eventName, object target, MethodInfo method, Func transfer) + { + EventName = eventName; + Target = target; + Method = method; + this.transfer = transfer; + } + + /// + /// 调用事件 + /// + /// 事件名 + /// 载荷 + public object Call(string eventName, params object[] payloads) + { + return transfer(eventName, payloads); + } + } +} diff --git a/src/CatLib.Core/Support/Events/IDispatcher.cs b/src/CatLib.Core/Support/Events/IDispatcher.cs new file mode 100644 index 0000000..26ebb53 --- /dev/null +++ b/src/CatLib.Core/Support/Events/IDispatcher.cs @@ -0,0 +1,69 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Reflection; + +namespace CatLib +{ + /// + /// 事件调度器 + /// + public interface IDispatcher + { + /// + /// 判断给定事件是否存在事件监听器 + /// + /// 事件名 + /// + /// 严格模式 + /// 启用严格模式则不使用正则来进行匹配事件监听器 + /// + /// 是否存在事件监听器 + bool HasListeners(string eventName, bool strict = false); + + /// + /// 触发一个事件,并获取事件监听器的返回结果 + /// + /// 事件名称 + /// 载荷 + /// 事件结果 + object[] Trigger(string eventName, params object[] payloads); + + /// + /// 触发一个事件,遇到第一个事件存在处理结果后终止,并获取事件监听器的返回结果 + /// + /// 事件名 + /// 载荷 + /// 事件结果 + object TriggerHalt(string eventName, params object[] payloads); + + /// + /// 注册一个事件监听器 + /// + /// 事件名称 + /// 调用目标 + /// 调用方法 + /// 事件对象 + IEvent On(string eventName, object target, MethodInfo method); + + /// + /// 解除注册的事件监听器 + /// + /// + /// 事件解除目标 + /// 如果传入的是字符串(string)将会解除对应事件名的所有事件 + /// 如果传入的是事件对象(IEvent)那么解除对应事件 + /// 如果传入的是其他实例(object)会解除该实例下的所有事件 + /// + void Off(object target); + } +} diff --git a/src/CatLib.Core/Support/Events/IEvent.cs b/src/CatLib.Core/Support/Events/IEvent.cs new file mode 100644 index 0000000..a29a1b3 --- /dev/null +++ b/src/CatLib.Core/Support/Events/IEvent.cs @@ -0,0 +1,44 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System.Reflection; + +namespace CatLib +{ + /// + /// 事件对象 + /// + public interface IEvent + { + /// + /// 事件名 + /// + string EventName { get; } + + /// + /// 事件目标 + /// + object Target { get; } + + /// + /// 方法信息 + /// + MethodInfo Method { get; } + + /// + /// 调用事件 + /// + /// 事件名 + /// 载荷 + /// 事件结果 + object Call(string eventName, params object[] payloads); + } +} diff --git a/src/CatLib.Core/Support/LruCache/CacheNode.cs b/src/CatLib.Core/Support/LruCache/CacheNode.cs deleted file mode 100644 index d75c501..0000000 --- a/src/CatLib.Core/Support/LruCache/CacheNode.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using System.Collections.Generic; - -namespace CatLib -{ - /// - /// 缓存节点 - /// - internal sealed class CacheNode - { - /// - /// 键值 - /// - public KeyValuePair KeyValue { get; private set; } - - /// - /// 上一个节点 - /// - public CacheNode Backward { get; set; } - - /// - /// 下一个节点 - /// - public CacheNode Forward { get; set; } - - /// - /// 创建一个缓存节点 - /// - /// 键 - /// 值 - public CacheNode(TKey key, TVal val) - { - KeyValue = new KeyValuePair(key, val); - } - - /// - /// 替换元素 - /// - /// 值 - public void Replace(TVal val) - { - KeyValue = new KeyValuePair(KeyValue.Key, val); - } - } -} \ No newline at end of file diff --git a/src/CatLib.Core/Support/LruCache/ILruCache.cs b/src/CatLib.Core/Support/LruCache/ILruCache.cs deleted file mode 100644 index a382191..0000000 --- a/src/CatLib.Core/Support/LruCache/ILruCache.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using System.Collections.Generic; - -namespace CatLib -{ - /// - /// Lru缓存 - /// - /// 键的类型 - /// 值的类型 - public interface ILruCache : IEnumerable> - { - /// - /// 在lru缓存中增加一个元素 - /// - /// 键 - /// 值 - void Add(TKey key, TVal value); - - /// - /// 根据key获取val,如果被淘汰则返回传入的默认值 - /// - /// 键 - /// 默认返回值 - /// - TVal Get(TKey key, TVal defaultValue = default(TVal)); - - /// - /// 根据key获取val,如果被淘汰则返回传入的默认值 - /// - /// 键 - /// 值 - /// 默认值 - /// 是否获取 - bool Get(TKey key, out TVal val, TVal defaultVal = default(TVal)); - - /// - /// 移除元素 - /// - /// 键 - void Remove(TKey key); - - /// - /// 获取Lru缓存中的元素数量 - /// - int Count { get; } - - /// - /// 根据key获取val,如果被淘汰则返回默认值 - /// - /// 键 - /// - TVal this[TKey key] { get; } - } -} \ No newline at end of file diff --git a/src/CatLib.Core/Support/LruCache/LruCache.cs b/src/CatLib.Core/Support/LruCache/LruCache.cs deleted file mode 100644 index ecc540f..0000000 --- a/src/CatLib.Core/Support/LruCache/LruCache.cs +++ /dev/null @@ -1,291 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; - -namespace CatLib -{ - /// - /// 近期最少使用缓存 - /// - [DebuggerDisplay("Count = {Count}")] - public sealed class LruCache : ILruCache - { - /// - /// 最大容量 - /// - private readonly int maxCapacity; - - /// - /// Lru Cache - /// - private readonly Dictionary> lruCache; - - /// - /// 头节点 - /// - private CacheNode header; - - /// - /// 尾节点 - /// - private CacheNode tail; - - /// - /// 当移除最后使用的元素之前 - /// - public event Action OnRemoveLeastUsed; - - /// - /// 近期最少使用缓存迭代器 - /// - private struct Enumerator : IEnumerable> - { - /// - /// 近期最少使用缓存 - /// - private readonly LruCache lruCache; - - /// - /// 构造一个迭代器 - /// - /// 近期最少使用缓存 - internal Enumerator(LruCache lruCache) - { - this.lruCache = lruCache; - } - - /// - /// 迭代器 - /// - /// 元素迭代器 - public IEnumerator> GetEnumerator() - { - var cursor = lruCache.header; - while (cursor != null) - { - yield return cursor.KeyValue; - cursor = cursor.Forward; - } - } - - /// - /// 获取迭代器 - /// - /// 迭代器 - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - /// - /// 创建一个Lru缓存 - /// - public LruCache(int maxCapacity) - { - Guard.Requires(maxCapacity > 0); - this.maxCapacity = maxCapacity; - lruCache = new Dictionary>(); - } - - /// - /// 在lru缓存中增加一个元素,如果元素已经存在则会替换元素 - /// - /// 键 - /// 值 - public void Add(TKey key, TVal value) - { - Guard.Requires(key != null); - CacheNode result; - if (lruCache.TryGetValue(key, out result)) - { - result.Replace(value); - MakeUsed(result); - return; - } - - if (lruCache.Count >= maxCapacity) - { - RemoveLeastUsed(); - } - - var addedNode = new CacheNode(key, value); - - if (header == null) - { - header = addedNode; - tail = addedNode; - } - else - { - MakeUsed(addedNode); - } - - lruCache.Add(key, addedNode); - } - - /// - /// 移除元素 - /// - /// 键 - public void Remove(TKey key) - { - Guard.Requires(key != null); - CacheNode result; - if (!lruCache.TryGetValue(key, out result)) - { - return; - } - lruCache.Remove(key); - if (result == tail) - { - tail = result.Backward; - } - if (result == header) - { - header = result.Forward; - } - if (result.Backward != null) - { - result.Backward.Forward = result.Forward; - } - } - - /// - /// 根据key获取val,如果被淘汰则返回传入的默认值 - /// - /// 键 - /// 默认返回值 - /// - public TVal Get(TKey key, TVal defaultValue = default(TVal)) - { - TVal result; - Get(key, out result, defaultValue); - return result; - } - - /// - /// 根据key获取val,如果被淘汰则返回传入的默认值 - /// - /// 键 - /// 值 - /// 默认值 - /// 是否获取 - public bool Get(TKey key, out TVal val, TVal defaultVal = default(TVal)) - { - Guard.Requires(key != null); - CacheNode result; - if (!lruCache.TryGetValue(key, out result)) - { - val = defaultVal; - return false; - } - - MakeUsed(result); - val = result.KeyValue.Value; - return true; - } - - /// - /// 获取Lru缓存中的元素数量 - /// - public int Count - { - get { return lruCache.Count; } - } - - /// - /// 根据key获取val,如果被淘汰则返回默认值 - /// - /// 键 - /// - public TVal this[TKey key] - { - get { return Get(key); } - set { Add(key, value); } - } - - /// - /// 迭代器 - /// - /// 迭代器 - public IEnumerator> GetEnumerator() - { - return new Enumerator(this).GetEnumerator(); - } - - /// - /// 迭代器 - /// - /// 迭代器 - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// 移除最后一个元素 - /// - private void RemoveLeastUsed() - { - if (OnRemoveLeastUsed != null) - { - OnRemoveLeastUsed.Invoke(tail.KeyValue.Key, tail.KeyValue.Value); - if (lruCache.Count < maxCapacity) - { - return; - } - } - - lruCache.Remove(tail.KeyValue.Key); - tail.Backward.Forward = null; - tail = tail.Backward; - } - - /// - /// 激活指定节点为最近使用 - /// - /// 节点 - private void MakeUsed(CacheNode node) - { - if (node.Forward == null && node.Backward == null) - { - node.Forward = header; - header.Backward = node; - if (header.Forward == null) - { - tail = header; - } - header = node; - } - else if (node.Forward == null && node.Backward != null) - { - node.Backward.Forward = null; - tail = node.Backward; - node.Forward = header; - header.Backward = node; - header = node; - } - else if (node.Forward != null && node.Backward != null) - { - node.Backward.Forward = node.Forward; - node.Forward.Backward = node.Backward; - node.Forward = header; - header.Backward = node; - header = node; - } - } - } -} \ No newline at end of file diff --git a/src/CatLib.Core/Support/Template/ISingleManaged.cs b/src/CatLib.Core/Support/Template/ISingleManaged.cs index 4b91ee1..e77f8cf 100644 --- a/src/CatLib.Core/Support/Template/ISingleManaged.cs +++ b/src/CatLib.Core/Support/Template/ISingleManaged.cs @@ -1,4 +1,14 @@ - +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + namespace CatLib { /// diff --git a/src/CatLib.Core/Support/Template/Managed.cs b/src/CatLib.Core/Support/Template/Managed.cs index 8830ada..d0da618 100644 --- a/src/CatLib.Core/Support/Template/Managed.cs +++ b/src/CatLib.Core/Support/Template/Managed.cs @@ -64,17 +64,6 @@ public bool ContainsExtend(string name = null) return resolve.ContainsKey(name); } - /// - /// 获取解决方案 - /// - /// 名字 - /// 拓展 - [Obsolete("Please use GetExtend()")] - protected Func GetResolve(string name) - { - return GetExtend(name); - } - /// /// 获取解决方案拓展 /// diff --git a/src/CatLib.Core/Support/Template/SingleManaged.cs b/src/CatLib.Core/Support/Template/SingleManaged.cs index de30c8e..2b5d9fd 100644 --- a/src/CatLib.Core/Support/Template/SingleManaged.cs +++ b/src/CatLib.Core/Support/Template/SingleManaged.cs @@ -1,4 +1,14 @@ - +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + using System.Collections.Generic; namespace CatLib diff --git a/src/CatLib.Core/Support/Util/Arr.cs b/src/CatLib.Core/Support/Util/Arr.cs index d070b77..11f1d93 100644 --- a/src/CatLib.Core/Support/Util/Arr.cs +++ b/src/CatLib.Core/Support/Util/Arr.cs @@ -10,7 +10,7 @@ */ using System; -using System.Diagnostics; +using System.Collections.Generic; namespace CatLib { @@ -230,6 +230,38 @@ public static T[] Fill(int start, int length, T value, T[] source = null) return requested; } + /// + /// 将数组每个值传给回调函数,如果回调函数返回 true,则移除数组中对应的元素,并返回被移除的元素 + /// + /// 数组类型 + /// 规定数组 + /// 回调函数 + /// 被移除的数组 + public static T[] Remove(ref T[] source, Predicate predicate) + { + Guard.Requires(source != null); + Guard.Requires(predicate != null); + + if (source.Length <= 0) + { + return new T[] { }; + } + + var results = new List(); + + for (var i = source.Length - 1; i >= 0; i--) + { + if (!predicate.Invoke(source[i])) + { + continue; + } + results.Add(source[i]); + RemoveAt(ref source, i); + } + + return Reverse(results.ToArray()); + } + /// /// 输入数组中的每个值传给回调函数,如果回调函数返回 true,则把输入数组中的当前值加入结果数组中 /// @@ -426,6 +458,12 @@ public static int Unshift(ref T[] source, params T[] elements) public static T[] Reverse(T[] source, int start = 0, int? length = null) { Guard.Requires(source != null); + + if (source.Length == 1) + { + return source; + } + Util.NormalizationPosition(source.Length, ref start, ref length); var tmpSource = new T[source.Length]; Array.Copy(source, tmpSource, source.Length); @@ -504,5 +542,60 @@ public static T[] Difference(T[] source, params T[] match) return true; }); } + + /// + /// 移除并返回指定下标的数组元素 + /// 如果下标传入的是负数那么将会从末尾移除 + /// + /// 数组类型 + /// 规定数组 + /// 数组下标 + /// 被移除的元素 + public static T RemoveAt(ref T[] source, int index) + { + Guard.Requires(source != null); + Guard.Requires(index < source.Length); + + var result = Splice(ref source, index, 1); + return result.Length > 0 ? result[0] : default(T); + } + + /// + /// 临时性的回调元素,如果遇到异常或者完成回调后会进行回滚元素回调 + /// + /// 数组类型 + /// 规定数组 + /// 顺序回调 + /// 所有回调完成后 + /// 回滚回调 + public static void Flash(T[] source, Action process, Action rollback, Action completed) + { + Guard.Requires(source != null); + + if (source.Length <= 0) + { + completed.Invoke(); + return; + } + + var index = 0; + try + { + foreach (var result in source) + { + ++index; + process.Invoke(result); + } + + completed(); + } + finally + { + while (--index >= 0) + { + rollback.Invoke(source[index]); + } + } + } } } \ No newline at end of file diff --git a/src/CatLib.Core/Support/Util/Dict.cs b/src/CatLib.Core/Support/Util/Dict.cs new file mode 100644 index 0000000..b08745b --- /dev/null +++ b/src/CatLib.Core/Support/Util/Dict.cs @@ -0,0 +1,342 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Collections.Generic; + +namespace CatLib +{ + /// + /// 字典 + /// + public static class Dict + { + /// + /// 将输入字典中的每个值传给回调函数,如果回调函数返回 true,则把输入字典中的当前键值对加入结果字典中 + /// + /// 字典键类型 + /// 字典值类型 + /// 规定字典 + /// 回调函数 + /// 需求字典 + public static IDictionary Filter(IDictionary source, + Func predicate) + { + Guard.Requires(source != null); + Guard.Requires(predicate != null); + var elements = new Dictionary(); + + foreach (var result in source) + { + if (predicate.Invoke(result.Key, result.Value)) + { + elements[result.Key] = result.Value; + } + } + + return elements; + } + + /// + /// 将输入字典中的每个值传给回调函数,如果回调函数返回 true,则移除字典中对应的元素,并返回被移除的元素 + /// + /// 字典键类型 + /// 字典值类型 + /// 规定字典 + /// 回调函数 + /// 被移除的元素 + public static KeyValuePair[] Remove(IDictionary source, Func predicate) + { + Guard.Requires(source != null); + Guard.Requires(predicate != null); + + var results = new List>(); + foreach (var result in source) + { + if (predicate.Invoke(result.Key, result.Value)) + { + results.Add(result); + } + } + + foreach (var result in results) + { + source.Remove(result.Key); + } + + return results.ToArray(); + } + + /// + /// 将输入字典中的每个值传给回调函数,回调函数的返回值用于修改元素的值 + /// + /// 字典键类型 + /// 字典值类型 + /// 规定字典 + /// 回调函数 + public static void Modify(IDictionary source, Func callback) + { + Guard.Requires(source != null); + Guard.Requires(callback != null); + + Dictionary elements = null; + foreach (var result in source) + { + var value = callback.Invoke(result.Key, result.Value); + if (!result.Equals(value)) + { + elements = elements ?? new Dictionary(); + elements[result.Key] = value; + } + } + + foreach (var result in elements) + { + source[result.Key] = result.Value; + } + } + + /// + /// 将元素批量添加到字典 + /// + /// 字典键 + /// 字典值 + /// 目标字典 + /// 增加的内容 + /// 遇到重复是否替换,如果不进行替换遇到重复将会抛出一个异常 + public static void AddRange(IDictionary source, IDictionary added, bool replaced = true) + { + Guard.Requires(source != null); + if (added == null) + { + return; + } + + foreach (var item in added) + { + if (replaced) + { + source[item.Key] = item.Value; + } + else + { + source.Add(item.Key, item.Value); + } + } + } + + /// + /// 将字典值传入用户自定义函数,自定义函数返回的值作为新的字典值 + /// + /// 字典键类型 + /// 字典值类型 + /// 规定字典 + /// 自定义函数 + /// 处理后的字典 + public static IDictionary Map(IDictionary source, Func callback) + { + Guard.Requires(source != null); + Guard.Requires(callback != null); + + var requested = new Dictionary(); + foreach (var result in source) + { + requested[result.Key] = callback.Invoke(result.Key, result.Value); + } + + return requested; + } + + /// + /// 获取字典的键数组 + /// + /// 字典键类型 + /// 字典值类型 + /// 规定字典 + /// 字典的键数组 + public static TKey[] Keys(IDictionary source) + { + Guard.Requires(source != null); + + var keys = new TKey[source.Count]; + var i = 0; + foreach (var item in source) + { + keys[i++] = item.Key; + } + + return keys; + } + + /// + /// 获取字典的值数组 + /// + /// 字典键类型 + /// 字典值类型 + /// 规定字典 + /// 字典的值数组 + public static TValue[] Values(IDictionary source) + { + Guard.Requires(source != null); + + var keys = new TValue[source.Count]; + var i = 0; + foreach (var item in source) + { + keys[i++] = item.Value; + } + + return keys; + } + + /// + /// 使用点(.)来访问深度字典 + /// + /// 规定字典 + /// 键,支持使用点(.)来进行深度访问 + /// 默认值 + /// 字典值 + public static object Get(IDictionary dict, string key, object def = null) + { + if (dict == null) + { + return def; + } + + if (string.IsNullOrEmpty(key)) + { + return dict; + } + + var keyArr = Arr.Reverse(key.Split('.')); + return GetValueByDepthArray(dict, ref keyArr) ?? def; + } + + /// + /// 使用点(.)来访问深度字典,并为其指定位置设定一个值 + /// + /// 规定字典 + /// 键,支持使用点(.)来进行深度访问 + /// 设定的值 + public static void Set(IDictionary dict, string key, object val) + { + Guard.Requires(dict != null); + Guard.Requires(key != null); + + var keyArr = Arr.Reverse(key.Split('.')); + SetValueByDepthArray(dict, ref keyArr, val); + } + + /// + /// 使用点(.)来访问深度字典,并移除其中指定的值 + /// + /// 规定字典 + /// 键,支持使用点(.)来进行深度访问 + public static bool Remove(IDictionary dict, string key) + { + Guard.Requires(dict != null); + Guard.Requires(key != null); + + var keyArr = Arr.Reverse(key.Split('.')); + return RemoveValueByDepthArray(dict, ref keyArr); + } + + /// + /// 通过深度数组来访问字典 + /// + /// 规定字典 + /// 深度数组(深度数组以倒序传入) + /// 字典值 + private static object GetValueByDepthArray(IDictionary dict, ref string[] keys) + { + while (true) + { + object result; + if (!dict.TryGetValue(Arr.Pop(ref keys), out result) || keys.Length <= 0) + { + return result; + } + + dict = result as IDictionary; + if (dict == null) + { + return null; + } + } + } + + /// + /// 通过深度数组来访问字典,并为其指定位置设定一个值 + /// + /// 规定字典 + /// 深度数组(深度数组以倒序传入) + /// 设定值 + private static void SetValueByDepthArray(IDictionary dict, ref string[] keys, object value) + { + while (true) + { + if (keys.Length <= 1) + { + dict[Arr.Pop(ref keys)] = value; + return; + } + + object result; + var key = Arr.Pop(ref keys); + if (!dict.TryGetValue(key, out result) || !(result is IDictionary)) + { + dict[key] = result = new Dictionary(); + } + + dict = (IDictionary)result; + } + } + + /// + /// 通过深度数组来移除数组中的一个值 + /// + /// 规定字典 + /// 深度数组(深度数组以倒序传入) + private static bool RemoveValueByDepthArray(IDictionary dict, ref string[] keys) + { + var perv = new Stack>>(keys.Length); + while (true) + { + if (keys.Length <= 1) + { + dict.Remove(Arr.Pop(ref keys)); + while (perv.Count > 0) + { + var data = perv.Pop(); + var tmpDict = (IDictionary)data.Value[data.Key]; + if (tmpDict.Count <= 0) + { + data.Value.Remove(data.Key); + continue; + } + break; + } + return true; + } + + object result; + var key = Arr.Pop(ref keys); + if (!dict.TryGetValue(key, out result) || !(result is IDictionary)) + { + return false; + } + + perv.Push(new KeyValuePair>(key, dict)); + dict = (IDictionary)result; + } + } + } +} diff --git a/src/CatLib.Core/Support/Util/Str.cs b/src/CatLib.Core/Support/Util/Str.cs index 521593e..a5103d3 100644 --- a/src/CatLib.Core/Support/Util/Str.cs +++ b/src/CatLib.Core/Support/Util/Str.cs @@ -10,6 +10,7 @@ */ using System; +using System.Collections.Specialized; using System.Text; using System.Text.RegularExpressions; @@ -41,6 +42,54 @@ public enum PadTypes Right } + /// + /// 获取字符串所表达的函数名 + /// + /// 输入字符串 + /// 函数名 + public static string Method(string pattern) + { + if (string.IsNullOrEmpty(pattern)) + { + return string.Empty; + } + + var chars = new char[pattern.Length]; + var count = 0; + for (var i = pattern.Length - 1; i >= 0; i--) + { + var segment = pattern[i]; + if ((segment >= 'A' && segment <= 'Z') + || (segment >= 'a' && segment <= 'z') + || (segment >= '0' && segment <= '9') + || segment == '_') + { + chars[count++] = segment; + continue; + } + + if (count > 0) + { + break; + } + } + + for (var i = count - 1; i >= 0; i--) + { + if ((chars[i] >= '0' && chars[i] <= '9')) + { + count--; + continue; + } + break; + } + + Array.Resize(ref chars, count); + Array.Reverse(chars); + + return new string(chars); + } + /// /// 将规定字符串翻译为星号匹配表达式 /// 即删减正则表达式中除了星号外的所有功能 @@ -67,18 +116,6 @@ public static string AsteriskWildcard(string pattern) return pattern; } - /// - /// 为每个正则表达式语法中的字符前增加一个反斜线。 - /// - /// 规定字符串 - /// 处理后的字符串 - [Obsolete("Please use Regex.Escape()")] - [ExcludeFromCodeCoverage] - public static string RegexQuote(string str) - { - return Regex.Escape(str); - } - /// /// 根据长度将字符串分割到数组中 /// diff --git a/src/CatLib.Core/Support/Util/Version.cs b/src/CatLib.Core/Support/Util/Version.cs index aa7bd59..43481ad 100644 --- a/src/CatLib.Core/Support/Util/Version.cs +++ b/src/CatLib.Core/Support/Util/Version.cs @@ -17,13 +17,28 @@ namespace CatLib /// /// 版本(遵循semver) /// - public sealed class Version + public class Version { /// /// 版本匹配正则式 /// - private static readonly Regex versionMatcher = new Regex( - @"^(?((?![0])\d+?|[0]))\.(?((?![0])\d+?|[0]))\.(?((?![0])\d+?|[0]))(?:-(?!\.)(?([a-zA-Z]\w*?|(?![0])\d+?|[0])(\.([a-zA-Z]\w*?|(?![0])\d+?|[0]))*?))?(?:\+(?!\.)(?([a-zA-Z]\w*?|(?![0])\d+?|[0])(\.([a-zA-Z]\w*?|(?![0])\d+?|[0]))*?))?$"); + private static Regex versionMatcher; + + /// + /// 版本匹配正则式 + /// + private static Regex VersionMatcher + { + get + { + if (versionMatcher == null) + { + versionMatcher = new Regex( + @"^(?((?![0])\d+?|[0]))\.(?((?![0])\d+?|[0]))\.(?((?![0])\d+?|[0]))(?:-(?!\.)(?([a-zA-Z]\w*?|(?![0])\d+?|[0])(\.([a-zA-Z]\w*?|(?![0])\d+?|[0]))*?))?(?:\+(?!\.)(?([a-zA-Z]\w*?|(?![0])\d+?|[0])(\.([a-zA-Z]\w*?|(?![0])\d+?|[0]))*?))?$"); + } + return versionMatcher; + } + } /// /// 原始版本信息 @@ -38,7 +53,7 @@ public sealed class Version /// /// 版本信息 /// - private struct VersionData + private class VersionData { /// /// 主版本号 @@ -71,7 +86,7 @@ private struct VersionData /// public VersionData(string version) { - var match = versionMatcher.Match(version); + var match = VersionMatcher.Match(version); Major = int.Parse(match.Groups["major"].ToString()); Minor = int.Parse(match.Groups["minor"].ToString()); Revised = int.Parse(match.Groups["revised"].ToString()); @@ -86,7 +101,7 @@ public VersionData(string version) /// 主版本号 /// 次版本号 /// 修订版本号 - public Version(int major, int minor, int revised) + public Version(int major, int minor, int revised) : this(major + "." + minor + "." + revised) { } @@ -97,9 +112,7 @@ public Version(int major, int minor, int revised) /// 版本号 public Version(string version) { - GuardVersion(version); this.version = version; - current = new VersionData(version); } /// @@ -112,6 +125,12 @@ public Version(string version) /// 比较结果 public int Compare(string version) { + if (current == null) + { + GuardVersion(this.version); + current = new VersionData(this.version); + } + GuardVersion(version); var compared = new VersionData(version); @@ -213,7 +232,7 @@ private int CompareBlock(string left, string right) /// 输入版本 private void GuardVersion(string version) { - if (!versionMatcher.IsMatch(version)) + if (!VersionMatcher.IsMatch(version)) { throw new RuntimeException("version is invalid"); }