IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 【DryIOC】封装器 -> 正文阅读

[开发测试]【DryIOC】封装器

DryIOC中,在使用一个或多个服务时是有用的数据结构。封装器使得使用主要包含两种类型:

  • 将待封装的服务作为开放式泛型的类型参数。例如:Func<TService>, Lazy<TService>, IEnumerable<TService>
  • 非开放式泛型使用必须的服务类型。例如:LambdaExpression

开放式泛型封装福哦个类型参数仅能封装一个服务类型。例如,Func<TArg0, TArg1, TService> 仅封装了 TService
在一个封装器中封装多个服务是不支持的

更多信息:

  • 封装器可以嵌套组合使用,例如:IEnumerable<Lazy<TService>>
  • Autofac相似的概念是relatiionship type
  • 支持预定义与用户定义的封装器
  • 显式的注册一个封装器类型作为一个服务重载对应的封装器注册
  • 常规的开放式泛型服务与封装器实际的不同将在用户定义封装器小节中解释

一个样例:

void Main()
{
	var container = new Container();

	// The actual service registrations
	container.Register<A>();
	container.Register<B>();

	// Lazy is available without registration!
	container.Resolve<B>();
}
class A { }
class B
{
	public B(Lazy<A> a) { }
}

1. 预定义封装器

预定义封装器总是可以的,不需要注册它们,但可以解除注册。预定义的封装器类型被DryIOC自动选择,来保持业务逻辑相对于某个特定IOC容器的依赖。这意味着除DryIoc.Meta<,>之外的所有封装器类型,在DryIOC之外都是可用的。

DryIoc.Meta<,>类型仅仅用于辅助从Autofac进行迁移,但可以使用System.Typle<,>进行替代。

1.1 Lazy of A

  • 在第一次访问时创建一个A的实例
  • 内部实现是调用r => new Lazy(() => r.Resolve<A>()),因此在Lazy注入时,A可能是不可用的
  • 允许递归依赖,因为在调用Resolve时,服务的创建被延迟

1.2 Func of A

  • 通过委托创建A到用户代码
  • 默认情况,服务通过内联服务进行创建:new B(() => new A())。因此,A需要在解析Func<A>时需要处于可用状态
  • 默认情况,不支持递归依赖
  • 作为替代,内联A创建以可改变到Resolve调用时:container.Register<A>(setup: Setup.With(asResolutionCall: true));
    • 这种方式Func<A>被注入通过:new B(() => r.Resolver.Resolve<A>())
    • 解析图中的递归依赖是被允许的

2. Lazy 与 Func的真实延迟

在解析LazyFunc时,被封装的依赖应该被注册到容器中,否则会报错。延迟是指延迟解析,而不是延迟注册。

void Main()
{
	var container = new Container();
	// Throws cause `A` is not registered
	container.Resolve<Lazy<A>>();
}
class A { }

延迟注册也是有方式进行的:

  • 通过RegisterPlaceHolder
  • 全局配置,Rules.WithFuncAndLazyWithoutRegistration()
void Main()
{
	var container = new Container(rules => rules.WithFuncAndLazyWithoutRegistration());
	var getA = container.Resolve<Func<A>>();

	// later register an `A`
	container.Register<A>();
	var a = getA();
}
class A { }

3. 带参数的Func

将服务的创建委托给用户,并允许提供依赖项的子集。其余依赖项通常由容器注入。更过信息:

  • 它可以被看作是通过参数提供的对通常注入的依赖项与自定义依赖项的重写。
  • 或者作为服务创建构造函数或方法的“柯里化”
  • 有时Func 带有基元类型的类型参数,用于传递值
  • 作为内联创建表达式注入new B((dep1, dep2) => new A(dep1, new D(), dep2))
  • 允许递归依赖

提供的参数按类型和传递顺序与参数匹配。如果未找到匹配的参数,则容器将注入该参数。参数顺序可能与参数顺序不同。如果提供的参数类型相同怎么办? DryIoc 将跟踪已使用的参数并在随后的匹配中跳过它们。意味着示例中的 dep1 和 dep2 可能是一个字符串并且仍然被使用。

如果未使用传递的参数,则 DryIoc 将继续匹配嵌套依赖项(如果有)cs new B(arg => new A(new D(arg)))

当未使用传递的参数时,它可能是一个错误,但也可能不是。这是一个艰难的选择,但 DryIoc 暂时会忽略这一点。

void Main()
{
	var container = new Container();
	container.Register<A>();

	// Can be used, the argument will be ignored
	var getA = container.Resolve<Func<string, A>>();

	getA("ignore me");
}
class A { }

还有一个与重用相关的警告。 您可能会说传递不同的参数来创建服务可能会产生不同的服务。 但是当服务注册为非瞬时重用时会发生什么? 默认情况下,DryIoc 将使用对 Func 的第一次调用来创建服务并忽略其余部分。

void Main()
{
	var container = new Container();
	container.Register<A>(Reuse.Singleton);

	// Can be used, the argument will be ignored
	var getA = container.Resolve<Func<string, A>>();
	Console.WriteLine("Hi, Alpha" == getA("Hi, Alpha").Greeting); // True
	
	// The result still be the same as before, so the second invocation and its arguments are ignored
	Console.WriteLine("Hi, Alpha" == getA("Hi, Beta").Greeting);  // True
}
class A
{
	public string Greeting { get; }
	public A(string greeting) { Greeting = greeting; }
}

如果你不喜欢这种方式,你可以使用规则 Rules.WithIgnoringReuseForFuncWithArgs()

void Main()
{
	var container = new Container(rules => rules.WithIgnoringReuseForFuncWithArgs());
	container.Register<A>(Reuse.Singleton);

	// Can be used, the argument will be ignored
	var getA = container.Resolve<Func<string, A>>();
	Console.WriteLine("Hi, Alpha" == getA("Hi, Alpha").Greeting); // True
	
	// The result still be the same as before, so the second invocation and its arguments are ignored
	Console.WriteLine("Hi, Alpha" == getA("Hi, Beta").Greeting);  // False
}
class A
{
	public string Greeting { get; }
	public A(string greeting) { Greeting = greeting; }
}

3.1 KeyValuePair of Service Key and A

将注册的服务密钥与解析的服务包装在一起。可用于根据其键过滤服务。通常与 Func 或 Lazy 包装器一起使用并嵌套在集合中:IEnumerable<KeyValuePair<string, Func<A>>>

3.2 Meta or Tuple of A with Metadata

将解析的服务 A 和关联的元数据对象打包在一起。有点类似于 KeyValuePair,但使用元数据而不是键。您可能会以与为实现类型定义的 Attribute 相同的方式考虑元数据。在MefAttributedModel时,元数据以特性的方式提供。

void Main()
{
	var container = new Container();
    container.Register<A>(setup: Setup.With(metadataOrFuncOfMetadata: "XYZ"));

    var a1 = container.Resolve<Meta<A, string>>();
    var a2 = container.Resolve<Tuple<A, string>>(); // is the same thing

	a1.Dump("a1");
	a2.Dump("a2");
}
class A{}

在这里插入图片描述

可以使用System.Tuple<,>替代DryIoc.Meta<,>,这样可以避免对DryIOC的引用。
如果没有在setup中提供元数据,而解析元数据,将抛出ContainerException。元数据亦可以被用作集合过滤。当元数据嵌套在集合封装器中式,此时元数据没有注册或不可赋值,没有异常被抛出。

void Main()
{
	var container = new Container();
    var items = container.Resolve<Meta<A, int>[]>();
    items.Dump();
}
class A { }

3.3 字典元数据

元数据值可以是非基元对象或 IDictionary<string, object>。在后一种情况下,您可以通过在字典中提供任何object类型来进行解析。

void Main()
{
	var container = new Container();
	container.Register<A>(setup: Setup.With(new Dictionary<string, object>{{ "color", "red" },{ "quantity", 15 }}));
	container.Register<A, B>(setup: Setup.With(new Dictionary<string, object>{{ "color", "red" },{ "special", true }}));

	// we have `A` and `B`
	var items = container.Resolve<Meta<A, IDictionary<string, object>>[]>();
	items.Dump();

	// here we have only `A` cause its metadata contains an `int` metadata value
	var itemQuantities = container.Resolve<Meta<A, int>[]>();
	itemQuantities.Dump();
}
class A { }
class B : A { }

3.4 IEnumerable or array of A

向使用者注入一组已注册的服务值。该集合基本上是一个完全初始化的“固定”数组:

var items = new B(new A[] { new Impl1(), new Impl2(), new Impl3() });

结果数组表达式仅使用所有找到的服务生成一次,如果之后添加新服务,或者更新或删除现有服务,则不会更改。您可以使用下面解释的 LazyEnumerable 来解决此限制。

4. 自定义封装器

要注册您自己的包装器,只需将设置参数指定为Setup.WrapperSetup.WrapperWith

void Main()
{
	var container = new Container();

    container.Register<ICmd, X>();
    container.Register<ICmd, Y>();

    // Register a wrapper
    container.Register(typeof(MenuItem<>), setup: Setup.Wrapper);
    Console.WriteLine(container.IsRegistered(typeof(MenuItem<>), factoryType: FactoryType.Wrapper));

    var items = container.Resolve<MenuItem<ICmd>[]>();
	items.Dump();
}
public interface ICmd { }
public class X : ICmd { }
public class Y : ICmd { }

// Here is the wrapper
public class MenuItem<TCmd> where TCmd : ICmd { }

包装器和非包装器之间的主要区别在于 ResolveMany 和集合包装器如何处理它们:

  • 当MenuItem正常注册时,数组将包含单个项目,因为单个MenuItem注册
  • 当 MenuItem 注册为包装器时,它会以特殊方式处理:解析器将首先尝试查找所有包装类型 (ICmd),然后将每个找到的类型包装在MenuItem 中。

4.1 非泛型包装器

void Main()
{
	{
		var container = new Container();

		container.Register<IService, Foo>();
		container.Register<IService, Bar>();
		container.Register<MyWrapper>(setup: Setup.Wrapper);
		Console.WriteLine(container.IsRegistered(typeof(MyWrapper), factoryType: FactoryType.Wrapper));

		var items = container.Resolve<MyWrapper[]>(requiredServiceType: typeof(IService));
		items.Dump();
	}

	{
		var container = new Container();

		container.Register<IService, Foo>();
		container.Register<IService, Bar>();
		container.Register<MyWrapper<IService>>(setup: Setup.Wrapper);
		Console.WriteLine(container.IsRegistered(typeof(MyWrapper<IService>), factoryType: FactoryType.Wrapper));

		var items = container.Resolve<MyWrapper<IService>[]>(); // you dont need the `requiredServiceType` here
		items.Dump();
	}
}
interface IService { }
class Foo : IService { }
class Bar : IService { }

class MyWrapper { public MyWrapper(IService service) { } }
class MyWrapper<T> { public MyWrapper(T service) { } }
  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-12-02 17:03:49  更:2021-12-02 17:04:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 4:18:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码