动手造轮子:实现一个简单的依赖注入(一)

动手造轮子:实现一个简单的依赖注入(一)

Intro

在上一篇文章中主要介绍了一下要做的依赖注入的整体设计和大概编程体验,这篇文章要开始写代码了,开始实现自己的依赖注入框架。

类图

首先来温习一下上次提到的类图

640?wx_fmt=png

服务生命周期

服务生命周期定义:

public enum ServiceLifetime : sbyte	
{	/// <summary>	/// Specifies that a single instance of the service will be created.	/// </summary>	Singleton = 0,	/// <summary>	/// Specifies that a new instance of the service will be created for each scope.	/// </summary>	Scoped = 1,	/// <summary>	/// Specifies that a new instance of the service will be created every time it is requested.	/// </summary>	Transient = 2,	
}

服务定义

服务注册定义:

public class ServiceDefinition	
{	// 服务生命周期	public ServiceLifetime ServiceLifetime { get; }	// 实现类型	public Type ImplementType { get; }	// 服务类型	public Type ServiceType { get; }	// 实现实例	public object ImplementationInstance { get; }	// 实现工厂	public Func<IServiceProvider, object> ImplementationFactory { get; }	// 获取真实的实现类型	public Type GetImplementType()	{	if (ImplementationInstance != null)	return ImplementationInstance.GetType();	if (ImplementationFactory != null)	return ImplementationFactory.Method.DeclaringType;	if (ImplementType != null)	return ImplementType;	return ServiceType;	}	public ServiceDefinition(object instance, Type serviceType)	{	ImplementationInstance = instance;	ServiceType = serviceType;	ServiceLifetime = ServiceLifetime.Singleton;	}	public ServiceDefinition(Type serviceType, ServiceLifetime serviceLifetime) : this(serviceType, serviceType, serviceLifetime)	{	}	public ServiceDefinition(Type serviceType, Type implementType, ServiceLifetime serviceLifetime)	{	ServiceType = serviceType;	ImplementType = implementType ?? serviceType;	ServiceLifetime = serviceLifetime;	}	public ServiceDefinition(Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime serviceLifetime)	{	ServiceType = serviceType;	ImplementationFactory = factory;	ServiceLifetime = serviceLifetime;	}	
}

为了使用起来更方便添加了一些静态方法

public static ServiceDefinition Singleton<TService>(Func<IServiceProvider, object> factory)	
{	return new ServiceDefinition(typeof(TService), factory, ServiceLifetime.Singleton);	
}	
public static ServiceDefinition Scoped<TService>(Func<IServiceProvider, object> factory)	
{	return new ServiceDefinition(typeof(TService), factory, ServiceLifetime.Scoped);	
}	
public static ServiceDefinition Transient<TService>(Func<IServiceProvider, object> factory)	
{	return new ServiceDefinition(typeof(TService), factory, ServiceLifetime.Transient);	
}	
public static ServiceDefinition Singleton<TService>()	
{	return new ServiceDefinition(typeof(TService), ServiceLifetime.Singleton);	
}	
public static ServiceDefinition Scoped<TService>()	
{	return new ServiceDefinition(typeof(TService), ServiceLifetime.Scoped);	
}	
public static ServiceDefinition Transient<TService>()	
{	return new ServiceDefinition(typeof(TService), ServiceLifetime.Transient);	
}	
public static ServiceDefinition Singleton<TService, TServiceImplement>() where TServiceImplement : TService	
{	return new ServiceDefinition(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Singleton);	
}	
public static ServiceDefinition Scoped<TService, TServiceImplement>() where TServiceImplement : TService	
{	return new ServiceDefinition(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Scoped);	
}	
public static ServiceDefinition Transient<TService, TServiceImplement>() where TServiceImplement : TService	
{	return new ServiceDefinition(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Transient);	
}

ServiceContainer

serviceContainer v1

public class ServiceContainer : IServiceContainer	
{	internal readonly List<ServiceDefinition> _services;	private readonly ConcurrentDictionary<Type, object> _singletonInstances;	private readonly ConcurrentDictionary<Type, object> _scopedInstances;	private readonly List<object> _transientDisposables = new List<object>();	private readonly bool _isRootScope;	public ServiceContainer()	{	_isRootScope = true;	_singletonInstances = new ConcurrentDictionary<Type, object>();	_services = new List<ServiceDefinition>();	}	internal ServiceContainer(ServiceContainer serviceContainer)	{	_isRootScope = false;	_singletonInstances = serviceContainer._singletonInstances;	_services = serviceContainer._services;	_scopedInstances = new ConcurrentDictionary<Type, object>();	}	public void Add(ServiceDefinition item)	{	_services.Add(item);	}	public IServiceContainer CreateScope()	{	return new ServiceContainer(this);	}	private bool _disposed;	public void Dispose()	{	if (_disposed)	{	return;	}	if (_isRootScope)	{	lock (_singletonInstances)	{	if (_disposed)	{	return;	}	_disposed = true;	foreach (var instance in _singletonInstances.Values)	{	(instance as IDisposable)?.Dispose();	}	foreach (var o in _transientDisposables)	{	(o as IDisposable)?.Dispose();	}	}	}	else	{	lock (_scopedInstances)	{	if (_disposed)	{	return;	}	_disposed = true;	foreach (var instance in _scopedInstances.Values)	{	(instance as IDisposable)?.Dispose();	}	foreach (var o in _transientDisposables)	{	(o as IDisposable)?.Dispose();	}	}	}	}	private object GetServiceInstance(Type serviceType, ServiceDefinition serviceDefinition)	{	if (serviceDefinition.ImplementationInstance != null)	return serviceDefinition.ImplementationInstance;	if (serviceDefinition.ImplementationFactory != null)	return serviceDefinition.ImplementationFactory.Invoke(this);	var implementType = (serviceDefinition.ImplementType ?? serviceType);	if (implementType.IsInterface || implementType.IsAbstract)	{	throw new InvalidOperationException($"invalid service registered, serviceType: {serviceType.FullName}, implementType: {serviceDefinition.ImplementType}");	}	var ctorInfos = implementType.GetConstructors(BindingFlags.Instance | BindingFlags.Public);	if (ctorInfos.Length == 0)	{	throw new InvalidOperationException($"service {serviceType.FullName} does not have any public constructors");	}	ConstructorInfo ctor;	if (ctorInfos.Length == 1)	{	ctor = ctorInfos[0];	}	else	{	// try find best ctor	ctor = ctorInfos	.OrderBy(_ => _.GetParameters().Length)	.First();	}	var parameters = ctor.GetParameters();	if (parameters.Length == 0)	{	// TODO: cache New Func	return Expression.Lambda<Func<object>>(Expression.New(ctor)).Compile().Invoke();	}	else	{	var ctorParams = new object[parameters.Length];	for (var index = 0; index < parameters.Length; index++)	{	var parameter = parameters[index];	var param = GetService(parameter.ParameterType);	if (param == null && parameter.HasDefaultValue)	{	param = parameter.DefaultValue;	}	ctorParams[index] = param;	}	return Expression.Lambda<Func<object>>(Expression.New(ctor, ctorParams.Select(Expression.Constant))).Compile().Invoke();	}	}	public object GetService(Type serviceType)	{	var serviceDefinition = _services.LastOrDefault(_ => _.ServiceType == serviceType);	if (null == serviceDefinition)	{	return null;	}	if (_isRootScope && serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)	{	throw new InvalidOperationException($"can not get scope service from the root scope, serviceType: {serviceType.FullName}");	}	if (serviceDefinition.ServiceLifetime == ServiceLifetime.Singleton)	{	var svc = _singletonInstances.GetOrAdd(serviceType, (t) => GetServiceInstance(t, serviceDefinition));	return svc;	}	else if (serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)	{	var svc = _scopedInstances.GetOrAdd(serviceType, (t) => GetServiceInstance(t, serviceDefinition));	return svc;	}	else	{	var svc = GetServiceInstance(serviceType, serviceDefinition);	if (svc is IDisposable)	{	_transientDisposables.Add(svc);	}	return svc;	}	}	
}

为了使得服务注册更加方便,可以写一些扩展方法来方便注册:

public static IServiceContainer AddSingleton<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]TService service)	
{	serviceContainer.Add(new ServiceDefinition(service, typeof(TService)));	return serviceContainer;	
}	
public static IServiceContainer AddSingleton([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType)	
{	serviceContainer.Add(new ServiceDefinition(serviceType, ServiceLifetime.Singleton));	return serviceContainer;	
}	
public static IServiceContainer AddSingleton([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType, [NotNull]Type implementType)	
{	serviceContainer.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Singleton));	return serviceContainer;	
}	
public static IServiceContainer AddSingleton<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]Func<IServiceProvider, object> func)	
{	serviceContainer.Add(ServiceDefinition.Singleton<TService>(func));	return serviceContainer;	
}	
public static IServiceContainer AddSingleton<TService>([NotNull]this IServiceContainer serviceContainer)	
{	serviceContainer.Add(ServiceDefinition.Singleton<TService>());	return serviceContainer;	
}	
public static IServiceContainer AddSingleton<TService, TServiceImplement>([NotNull]this IServiceContainer serviceContainer) where TServiceImplement : TService	
{	serviceContainer.Add(ServiceDefinition.Singleton<TService, TServiceImplement>());	return serviceContainer;	
}	
public static IServiceContainer AddScoped([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType)	
{	serviceContainer.Add(new ServiceDefinition(serviceType, ServiceLifetime.Scoped));	return serviceContainer;	
}	
public static IServiceContainer AddScoped([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType, [NotNull]Type implementType)	
{	serviceContainer.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Scoped));	return serviceContainer;	
}	
public static IServiceContainer AddScoped<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]Func<IServiceProvider, object> func)	
{	serviceContainer.Add(ServiceDefinition.Scoped<TService>(func));	return serviceContainer;	
}	
public static IServiceContainer AddScoped<TService>([NotNull]this IServiceContainer serviceContainer)	
{	serviceContainer.Add(ServiceDefinition.Scoped<TService>());	return serviceContainer;	
}	
public static IServiceContainer AddScoped<TService, TServiceImplement>([NotNull]this IServiceContainer serviceContainer) where TServiceImplement : TService	
{	serviceContainer.Add(ServiceDefinition.Scoped<TService, TServiceImplement>());	return serviceContainer;	
}	
public static IServiceContainer AddTransient([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType)	
{	serviceContainer.Add(new ServiceDefinition(serviceType, ServiceLifetime.Transient));	return serviceContainer;	
}	
public static IServiceContainer AddTransient([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType, [NotNull]Type implementType)	
{	serviceContainer.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Transient));	return serviceContainer;	
}	
public static IServiceContainer AddTransient<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]Func<IServiceProvider, object> func)	
{	serviceContainer.Add(ServiceDefinition.Transient<TService>(func));	return serviceContainer;	
}	
public static IServiceContainer AddTransient<TService>([NotNull]this IServiceContainer serviceContainer)	
{	serviceContainer.Add(ServiceDefinition.Transient<TService>());	return serviceContainer;	
}	
public static IServiceContainer AddTransient<TService, TServiceImplement>([NotNull]this IServiceContainer serviceContainer) where TServiceImplement : TService	
{	serviceContainer.Add(ServiceDefinition.Transient<TService, TServiceImplement>());	return serviceContainer;	
}

通过上面的代码就可以实现基本依赖注入了,但是从功能上来说,上面的代码只支持获取单个服务的实例,不支持注册一个接口的多个实现,获取接口的所有实现,为此对 ServiceContainer 中的 Instance 的 ConcurrentDictionary 的 Key 进行一下改造,使得可以能够以接口类型和实现类型联合作为 key,为此就有了第二版的 ServiceContainer

ServiceContainer v2

为此定义了一个 ServiceKey 的类型,请注意这里一定要重写 GetHashCode 方法:

private class ServiceKey : IEquatable<ServiceKey>	
{	public Type ServiceType { get; }	public Type ImplementType { get; }	public ServiceKey(Type serviceType, ServiceDefinition definition)	{	ServiceType = serviceType;	ImplementType = definition.GetImplementType();	}	public bool Equals(ServiceKey other)	{	return ServiceType == other?.ServiceType && ImplementType == other?.ImplementType;	}	public override bool Equals(object obj)	{	return Equals((ServiceKey)obj);	}	public override int GetHashCode()	{	var key = $"{ServiceType.FullName}_{ImplementType.FullName}";	return key.GetHashCode();	}	
}

第二版的 ServiceContainer :

public class ServiceContainer : IServiceContainer	
{	internal readonly ConcurrentBag<ServiceDefinition> _services;	private readonly ConcurrentDictionary<ServiceKey, object> _singletonInstances;	private readonly ConcurrentDictionary<ServiceKey, object> _scopedInstances;	private ConcurrentBag<object> _transientDisposables = new ConcurrentBag<object>();	private class ServiceKey : IEquatable<ServiceKey>	{	public Type ServiceType { get; }	public Type ImplementType { get; }	public ServiceKey(Type serviceType, ServiceDefinition definition)	{	ServiceType = serviceType;	ImplementType = definition.GetImplementType();	}	public bool Equals(ServiceKey other)	{	return ServiceType == other?.ServiceType && ImplementType == other?.ImplementType;	}	public override bool Equals(object obj)	{	return Equals((ServiceKey)obj);	}	public override int GetHashCode()	{	var key = $"{ServiceType.FullName}_{ImplementType.FullName}";	return key.GetHashCode();	}	}	private readonly bool _isRootScope;	public ServiceContainer()	{	_isRootScope = true;	_singletonInstances = new ConcurrentDictionary<ServiceKey, object>();	_services = new ConcurrentBag<ServiceDefinition>();	}	private ServiceContainer(ServiceContainer serviceContainer)	{	_isRootScope = false;	_singletonInstances = serviceContainer._singletonInstances;	_services = serviceContainer._services;	_scopedInstances = new ConcurrentDictionary<ServiceKey, object>();	}	public IServiceContainer Add(ServiceDefinition item)	{	if (_disposed)	{	throw new InvalidOperationException("the service container had been disposed");	}	if (_services.Any(_ => _.ServiceType == item.ServiceType && _.GetImplementType() == item.GetImplementType()))	{	return this;	}	_services.Add(item);	return this;	}	public IServiceContainer TryAdd(ServiceDefinition item)	{	if (_disposed)	{	throw new InvalidOperationException("the service container had been disposed");	}	if (_services.Any(_ => _.ServiceType == item.ServiceType))	{	return this;	}	_services.Add(item);	return this;	}	public IServiceContainer CreateScope()	{	return new ServiceContainer(this);	}	private bool _disposed;	public void Dispose()	{	if (_disposed)	{	return;	}	if (_isRootScope)	{	lock (_singletonInstances)	{	if (_disposed)	{	return;	}	_disposed = true;	foreach (var instance in _singletonInstances.Values)	{	(instance as IDisposable)?.Dispose();	}	foreach (var o in _transientDisposables)	{	(o as IDisposable)?.Dispose();	}	_singletonInstances.Clear();	_transientDisposables = null;	}	}	else	{	lock (_scopedInstances)	{	if (_disposed)	{	return;	}	_disposed = true;	foreach (var instance in _scopedInstances.Values)	{	(instance as IDisposable)?.Dispose();	}	foreach (var o in _transientDisposables)	{	(o as IDisposable)?.Dispose();	}	_scopedInstances.Clear();	_transientDisposables = null;	}	}	}	private object GetServiceInstance(Type serviceType, ServiceDefinition serviceDefinition)	{	if (serviceDefinition.ImplementationInstance != null)	return serviceDefinition.ImplementationInstance;	if (serviceDefinition.ImplementationFactory != null)	return serviceDefinition.ImplementationFactory.Invoke(this);	var implementType = (serviceDefinition.ImplementType ?? serviceType);	if (implementType.IsInterface || implementType.IsAbstract)	{	throw new InvalidOperationException($"invalid service registered, serviceType: {serviceType.FullName}, implementType: {serviceDefinition.ImplementType}");	}	if (implementType.IsGenericType)	{	implementType = implementType.MakeGenericType(serviceType.GetGenericArguments());	}	var ctorInfos = implementType.GetConstructors(BindingFlags.Instance | BindingFlags.Public);	if (ctorInfos.Length == 0)	{	throw new InvalidOperationException($"service {serviceType.FullName} does not have any public constructors");	}	ConstructorInfo ctor;	if (ctorInfos.Length == 1)	{	ctor = ctorInfos[0];	}	else	{	// TODO: try find best ctor	ctor = ctorInfos	.OrderBy(_ => _.GetParameters().Length)	.First();	}	var parameters = ctor.GetParameters();	if (parameters.Length == 0)	{	// TODO: cache New Func	return Expression.Lambda<Func<object>>(Expression.New(ctor)).Compile().Invoke();	}	else	{	var ctorParams = new object[parameters.Length];	for (var index = 0; index < parameters.Length; index++)	{	var parameter = parameters[index];	var param = GetService(parameter.ParameterType);	if (param == null && parameter.HasDefaultValue)	{	param = parameter.DefaultValue;	}	ctorParams[index] = param;	}	return Expression.Lambda<Func<object>>(Expression.New(ctor, ctorParams.Select(Expression.Constant))).Compile().Invoke();	}	}	public object GetService(Type serviceType)	{	if (_disposed)	{	throw new InvalidOperationException($"can not get scope service from a disposed scope, serviceType: {serviceType.FullName}");	}	var serviceDefinition = _services.LastOrDefault(_ => _.ServiceType == serviceType);	if (null == serviceDefinition)	{	if (serviceType.IsGenericType)	{	var genericType = serviceType.GetGenericTypeDefinition();	serviceDefinition = _services.LastOrDefault(_ => _.ServiceType == genericType);	if (null == serviceDefinition)	{	var innerServiceType = serviceType.GetGenericArguments().First();	if (typeof(IEnumerable<>).MakeGenericType(innerServiceType)	.IsAssignableFrom(serviceType))	{	var innerRegType = innerServiceType;	if (innerServiceType.IsGenericType)	{	innerRegType = innerServiceType.GetGenericTypeDefinition();	}	//	var list = new List<object>(4);	foreach (var def in _services.Where(_ => _.ServiceType == innerRegType))	{	object svc;	if (def.ServiceLifetime == ServiceLifetime.Singleton)	{	svc = _singletonInstances.GetOrAdd(new ServiceKey(innerServiceType, def), (t) => GetServiceInstance(innerServiceType, def));	}	else if (def.ServiceLifetime == ServiceLifetime.Scoped)	{	svc = _scopedInstances.GetOrAdd(new ServiceKey(innerServiceType, def), (t) => GetServiceInstance(innerServiceType, def));	}	else	{	svc = GetServiceInstance(innerServiceType, def);	if (svc is IDisposable)	{	_transientDisposables.Add(svc);	}	}	if (null != svc)	{	list.Add(svc);	}	}	var methodInfo = typeof(Enumerable)	.GetMethod("Cast", BindingFlags.Static | BindingFlags.Public);	if (methodInfo != null)	{	var genericMethod = methodInfo.MakeGenericMethod(innerServiceType);	var castedValue = genericMethod.Invoke(null, new object[] { list });	if (typeof(IEnumerable<>).MakeGenericType(innerServiceType) == serviceType)	{	return castedValue;	}	var toArrayMethod = typeof(Enumerable).GetMethod("ToArray", BindingFlags.Static | BindingFlags.Public)	.MakeGenericMethod(innerServiceType);	return toArrayMethod.Invoke(null, new object[] { castedValue });	}	return list;	}	return null;	}	}	else	{	return null;	}	}	if (_isRootScope && serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)	{	throw new InvalidOperationException($"can not get scope service from the root scope, serviceType: {serviceType.FullName}");	}	if (serviceDefinition.ServiceLifetime == ServiceLifetime.Singleton)	{	var svc = _singletonInstances.GetOrAdd(new ServiceKey(serviceType, serviceDefinition), (t) => GetServiceInstance(t.ServiceType, serviceDefinition));	return svc;	}	else if (serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)	{	var svc = _scopedInstances.GetOrAdd(new ServiceKey(serviceType, serviceDefinition), (t) => GetServiceInstance(t.ServiceType, serviceDefinition));	return svc;	}	else	{	var svc = GetServiceInstance(serviceType, serviceDefinition);	if (svc is IDisposable)	{	_transientDisposables.Add(svc);	}	return svc;	}	}	
}

这样我们就不仅支持了 IEnumerable<TService> 的注册,也支持 IReadOnlyList<TService>/ IReadOnlyCollection<TService> 的注册

因为 GetService 返回是 object , 不是强类型的,所以为了使用起来方便,定义了几个扩展方法,类似于微软的依赖注入框架里的 GetService<TService>()/ GetServices<TService>()/ GetRequiredService<TService>()

/// <summary>	
/// ResolveService	
/// </summary>	
/// <typeparam name="TService">TService</typeparam>	
/// <param name="serviceProvider">serviceProvider</param>	
/// <returns></returns>	
public static TService ResolveService<TService>([NotNull]this IServiceProvider serviceProvider)	=> (TService)serviceProvider.GetService(typeof(TService));	
/// <summary>	
/// ResolveRequiredService	
/// throw exception if can not get a service instance	
/// </summary>	
/// <typeparam name="TService">TService</typeparam>	
/// <param name="serviceProvider">serviceProvider</param>	
/// <returns></returns>	
public static TService ResolveRequiredService<TService>([NotNull] this IServiceProvider serviceProvider)	
{	var serviceType = typeof(TService);	var svc = serviceProvider.GetService(serviceType);	if (null == svc)	{	throw new InvalidOperationException($"service had not been registered, serviceType: {serviceType}");	}	return (TService)svc;	
}	
/// <summary>	
/// Resolve services	
/// </summary>	
/// <typeparam name="TService">TService</typeparam>	
/// <param name="serviceProvider">serviceProvider</param>	
/// <returns></returns>	
public static IEnumerable<TService> ResolveServices<TService>([NotNull]this IServiceProvider serviceProvider)	=> serviceProvider.ResolveService<IEnumerable<TService>>();

More

后面还更新了一版,主要优化性能,目前来说还不太满意,暂时这里先不提了

Reference

  • Dynamic Casting using Reflection

  • https://github.com/WeihanLi/WeihanLi.Common/blob/dev/test/WeihanLi.Common.Test/DependencyInjectionTest.cs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313195.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt 调试Caffe

http://blog.csdn.net/xg123321123/article/details/52817658 1.下载并安装Qt Creator 下载页面&#xff0c;推荐使用4.x版本&#xff0c;比如&#xff1a; Qt Creator 4.1.0 for Linux 64-bit下载的是run包&#xff0c;安装方法&#xff1a; cd到下载目录 chmod x xxx.run …

.NET Core 3.0 本地工具

.NET Core从最早期的版本就开始支持全局工具了。如果仅仅需要在某个项目中或某个文件夹中使用特定的工具&#xff0c;那么.NET Core 3.0就允许您这样做。 使用.NET Core 3.0&#xff0c;您可以在特定的文件夹下安装“本地”工具&#xff0c;它的作用范围仅限于该文件夹及其子文…

Nsight 调试 Caffe

http://blog.csdn.net/u014114990/article/details/47723877 右键属性菜单的General->Code Analysis->Paths and Symbols下进行加入&#xff1a; Includes下加入程序需要用到的头文件的路径&#xff1a; Library Path下添加需要用到库文件的路径&#xff1a; 具体用到的…

张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出

什么是 PWM在解释 PWM 之前首先来了解一下电路中信号的概念&#xff0c;其中包括模拟信号和数字信号。模拟信号是一种连续的信号&#xff0c;与连续函数类似&#xff0c;在图形上表现为一条不间断的连续曲线。数字信号为只能取有限个数值的信号&#xff0c;比如计算机中的高电平…

.NET Core 3 对 IoT 应用程序的高级支持:System.Device.Gpio

System.Device.Gpio 是一个全新的 .Net Core 开源库&#xff0c;它旨在使 IoT&#xff08;物联网&#xff09;应用程序能够通过其 GPIO 引脚或其他 I/O 控制硬件与传感器、显示器和输入设备进行交互。该库是由社区维护的多个设备绑定集合来进行增强实现的。正如微软 .NET 项目…

代码阅读2

http://blog.csdn.net/u014568921/article/details/53995455 首先&#xff0c;要知道caffe里的卷积核都是三维的 在caffe中卷积核是三维的还是二维的&#xff1f; 下面分割线之间的内容来自http://blog.csdn.NET/u014114990/article/details/51125776 /*********************…

一文带你了解华为云DevCloud为何能全面领跑中国DevOps云服务市场

近日&#xff0c;国际权威调研机构IDC发布了《IDC MarketScape&#xff1a;中国DevOps云服务市场2019厂商评估》报告&#xff0c;该报告从战略和能力两个维度对国内主流DevOps云厂商进行了评估&#xff0c;报告显示&#xff0c;华为云位于 IDC MarketScape “中国DevOps云服务 …

[电子书制作]Excel催化剂输出内容汇总PDF及Word版本分享

Excel催化剂在2018年开始&#xff0c;陆续写出了230篇高质量原创性文章&#xff0c;将Excel催化剂插件的开发过程及使用方法全方位地通过文字的方式给广大网友们分享了。电子书下载方式同样地&#xff0c;为了减少大家过多繁琐的资料下载途径&#xff0c;电子书的下载路径和之前…

深入理解.NET Core的基元: deps.json, runtimeconfig.json, dll文件

原文链接&#xff1a; Deep-dive into .NET Core primitives: deps.json, runtimeconfig.json, and dlls作者&#xff1a; Nate McMasterC#编译器(The C# Compiler)C#的编译器可以将cs文件转换为dll文件, 即程序集文件。程序集文件是一个便携的可执行格式文件, 借助.NET Core,它…

C# 8.0 的默认接口方法

例子直接看例子有这样一个接口&#xff1a; 然后有三个它的实现类&#xff1a; 然后在main方法里面调用&#xff1a; 截至目前&#xff0c;程序都可以成功的编译和运行。 IPerson接口变更 突然&#xff0c;我想对所有的人类添加一个新的特性&#xff0c;例如&#xff0c;添加一…

convolutional layer 源代码

http://blog.csdn.net/xizero00/article/details/51049858 一、 卷积层的作用简介 卷积层是深度神经网络中的一个重要的层&#xff0c;该层实现了局部感受野&#xff0c;通过这种局部感受野&#xff0c;可以有效地降低参数的数目。我们将结合caffe来讲解具体是如何实现卷积层的…

在.net core3.0中使用SignalR实现实时通信

最近用.net core3.0重构网站&#xff0c;老大想做个站内信功能&#xff0c;就是有些耗时的后台任务的结果需要推送给用户。一开始我想简单点&#xff0c;客户端每隔1分钟调用一下我的接口&#xff0c;看看是不是有新消息&#xff0c;有的话就告诉用户有新推送&#xff0c;但老大…

活动最后72小时:购书优惠劵,折后再折,赶紧来抢啊

1024程序员节当当网计算机图书每满100减50&#xff01;满200减100&#xff01;满300-150&#xff01;机械工业出版社华章公司联合当当网特意为【DotNET技术圈】用户申请了一批可与满减叠加使用的“满200减30”的图书优惠码&#xff0c;优惠码使用后相当于&#xff1a;400减230 …

C# 8 新特性 - using 声明

using语句块 尽管.NET Core运行时有垃圾收集器&#xff08;GC&#xff09;来负责内存清理工作&#xff0c;但是我们还是要自己确保当非托管资源不再使用的时候应该被清理掉。以前针对实现了IDisposable接口的对象&#xff0c;我们经常会使用using 语句块来这样做&#xff1a; 这…

.Net Core3.0依赖注入DI

构建ASP.NET Core应用程序的时候&#xff0c;依赖注入已成为了.NET Core的核心&#xff0c;这篇文章&#xff0c;我们理一理依赖注入的使用方法。不使用依赖注入首先&#xff0c;我们创建一个ASP.NET Core Mvc项目&#xff0c;定义个表达的爱服务接口&#xff0c;中国小伙类实现…

.Net轻量状态机Stateless

很多业务系统开发中&#xff0c;不可避免的会出现状态变化&#xff0c;通常采用的情形可能是使用工作流去完成&#xff0c;但是对于简单场景下&#xff0c;用工作流有点大财小用感觉&#xff0c;比如订单业务中&#xff0c;订单状态的变更&#xff0c;涉及到的状态量不是很多&a…

Asp.net Core全局异常监控和记录日志

前言系统异常监控可以说是重中之重&#xff0c;系统不可能一直运行良好&#xff0c;开发和运维也不可能24小时盯着系统&#xff0c;系统抛异常后我们应当在第一时间收到异常信息。在Asp.net Core里我使用拦截器和中间件两种方式来监控异常。全局异常监控的数据最好还是写入数据…

SiteServer CMS 新版本 V6.13(2019年11月1日发布)

欢迎来到 SiteServer CMS V6.13 版本&#xff0c;经过两个月的连续迭代开发&#xff0c;V6.13版本新增了几项重要功能&#xff0c;我们希望你会喜欢&#xff0c;一些关键的亮点包括&#xff1a;。新增功能及BUG 修复经过两个月的连续迭代开发&#xff0c;V6.13 版本新增了部分功…

CUDA的global内存访问的问题

http://blog.csdn.net/OpenHero/article/details/3520578 关于CUDA的global内存访问的问题&#xff0c;怎么是访问的冲突&#xff0c;怎样才能更好的访问内存&#xff0c;达到更高的速度。下面先看几张图&#xff0c;这些图都是CUDA编程手册上的图&#xff0c;然后分别对这些…