.net core发布有一段时间了,最近两个月开始使用.net core2.0开发项目,大大小小遇到了一些问题。准备写个系列介绍一下是如何解决这些问题以及对应技术。先从IOC容器Autofac开始该系列。

阅读目录

Autofac基本使用

  Autofac是一款轻量级的IOC框架,使用率上还是挺高的,官方网站http://autofac.org,源码下载地址https://github.com/autofac/Autofac

  下面以狗的列子来介绍autofac,nuget搜索Autofac进行安装

    public interface IDog
{
/// <summary>
/// 品种
/// </summary>
string Breed { get; } /// <summary>
/// 名称
/// </summary>
string Name { get; }
} /// <summary>
/// 萨摩耶
/// </summary>
public class Samoyed : IDog
{
/// <summary>
/// 品种
/// </summary>
public string Breed
{
get
{
return "Samoyed(萨摩耶)";
}
} /// <summary>
/// 名称
/// </summary>
public string Name
{
get
{
return "小黄";
}
}
} /// <summary>
/// 藏獒
/// </summary>
public class TibetanMastiff : IDog
{
/// <summary>
/// 品种
/// </summary>
public string Breed
{
get
{
return "Mastiff Class(獒犬类)";
}
} /// <summary>
/// 名称
/// </summary>
public string Name
{
get
{
return "小黑";
}
}
}

  1.RegisterType 

public static void Register()
{
var builder = new ContainerBuilder();
//注册Samoyed指定为IDog实现
builder.RegisterType<Samoyed>().As<IDog>();
builder.RegisterType<TibetanMastiff>().As<IDog>();
using (var container = builder.Build())
{
var dogs = container.Resolve<IEnumerable<IDog>>();
foreach (var dog in dogs)
{
Console.WriteLine($"名称:{dog.Name},品种:{dog.Breed}");
}
}
}
  2.RegisterAssemblyTypes
public static void RegisterAssemblyTypes()
{
var builder = new ContainerBuilder();
//注册程序集下所有类型
builder.RegisterAssemblyTypes(typeof(Program).Assembly).AsImplementedInterfaces();
using (var container = builder.Build())
{
var dogs = container.Resolve<IEnumerable<IDog>>();
foreach (var dog in dogs)
{
Console.WriteLine($"名称:{dog.Name},品种:{dog.Breed}");
}
}
}

  直接注册程序集下的所有类型,AsImplementedInterfaces(让具体实现类型,可以该类型继承的所有接口类型找到该实现类型)

  3.RegisterInstance

TibetanMastiff d = new TibetanMastiff();
builder.RegisterInstance(d).As<IDog>();

  4.RegisterModule

  这种模式需要使用配置文件进行注册,个人更喜欢代码直接注册的方式,毕竟配置文件修改容易遗忘和出错。这里就不介绍该方式了。

  遗留问题:上面的注册代码,自己写写demo的时候没啥问题。但是运用到项目里面就很繁琐了,需要自己一个个类型注册,后面会提供解决方案。

 

.net core MVC与Autofac

  1.首先nuget下载Autofac和Autofac.Extensions.DependencyInjection引用

  2.替换mvc自带的DI框架

  将Startup.cs中的ConfigureServices返回类型改为IServiceProvider

public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc(); var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterAssemblyTypes(typeof(Startup).Assembly).AsImplementedInterfaces();
var Container = builder.Build();
return new AutofacServiceProvider(Container);
}

属性注入

Autofac默认是构造函数注入

[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly IEnumerable<IDog> dogs; public ValuesController(IEnumerable<IDog> _dogs)
{
dogs = _dogs;
} // GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
List<string> list = new List<string>();
foreach (var dog in dogs)
{
list.Add($"名称:{dog.Name},品种:{dog.Breed}");
}
return list.ToArray(); ;
}
}
 
使用过mef的可能更喜欢属性注入的方式,那么使用autofac怎么实现属性注入呢?
1.注册系统所有Controller,由Autofac创建
var IControllerType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(assembly).Where(t =>
IControllerType.IsAssignableFrom(t) && t != IControllerType).PropertiesAutowired();

上面这段代码的解释:注册所有程序集下继承ControllerBase的类型,PropertiesAutowired 允许属性注入。

2.替换系统默认Controller创建器

services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
services.AddMvc();
注意:Replace代码放在AddMvc之前
Replace代码的意思:使用ServiceBasedControllerActivator替换DefaultControllerActivator(意味着框架现在会尝试从IServiceProvider中解析控制器实例,也就是return new AutofacServiceProvider(Container);
3.使用属性注入
  [Route("api/[controller]")]
public class ValuesController : Controller
{
public IEnumerable<IDog> dogs { get; set; }
[HttpGet]
public IEnumerable<string> Get()
{
List<string> list = new List<string>();
foreach (var dog in dogs)
{
list.Add($"名称:{dog.Name},品种:{dog.Breed}");
}
return list.ToArray(); ;
}
}
至此完成了使用Autofac实现属性注入

Autofac+Castle实现AOP

1.首先nuget下载Autofac.Extras.DynamicProxy引用
2.编写拦截器
public class LogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("你正在调用方法 \"{0}\" 参数是 {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())); invocation.Proceed();
if (invocation.ReturnValue != null && invocation.ReturnValue is string)
{
//在返回接口上拼上LogInterceptor
invocation.ReturnValue += " LogInterceptor";
}
Console.WriteLine("方法执行完毕,返回结果:{0}", invocation.ReturnValue); Console.WriteLine("开始记录日志....");
}
}
3.开启拦截(接口拦截器  类拦截器)
builder.RegisterType<LogInterceptor>();
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces()
.EnableInterfaceInterceptors();
var IControllerType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(assembly).Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).PropertiesAutowired()
.EnableClassInterceptors();
var Container = builder.Build();
开启接口拦截器:EnableInterfaceInterceptors  开启类拦截器:EnableClassInterceptors
[Intercept(typeof(LogInterceptor))]
[Route("api/[controller]")]
public class ValuesController : Controller
{
}
[Intercept(typeof(LogInterceptor))]
public class Samoyed : IDog
{
}
这种使用方式需要自己指定在哪个类上使用,还有一种全局拦截器
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().EnableInterfaceInterceptors()
.InterceptedBy(typeof(LogInterceptor));

代码封装简单使用

先列出使用过程中遇到的几个问题,然后再给出解决方案

1:如何简单注册代码里面的所有类型

2.如何注册单例和普通对象

3.封装好的代码怎么支持用户特殊化注册需求

为了解决上述问题,这里给出了几个约束

单例对象需继承的接口:ISingletonDependency  普通对象需继承的接口:ITransientDependency 特殊化注册接口:IDependencyRegistrar

通过这几个约束,在初始化时找所有程序集 继承ISingletonDependency ,ITransientDependency 接口的对象进行类型注册

    /// <summary>
/// 单例接口
/// </summary>
public interface ISingletonDependency
{
}
    /// <summary>
/// 所有接口的依赖接口,每次创建新实例
/// </summary>
/// <remarks>
/// 用于Autofac自动注册时,查找所有依赖该接口的实现。
/// 实现自动注册功能
/// </remarks>
public interface ITransientDependency
{
}
    /// <summary>
/// 依赖注册接口
/// </summary>
public interface IDependencyRegistrar
{
/// <summary>
/// Register services and interfaces
/// </summary>
/// <param name="builder">Container builder</param>
/// <param name="config">Config</param>
void Register(ContainerBuilder builder,List<Type> listType); /// <summary>
/// Order of this dependency registrar implementation
/// </summary>
int Order { get; }
}
 public interface IIocManager
{
IContainer Container { get; } bool IsRegistered(Type serviceType, ILifetimeScope scope = null);
object Resolve(Type type, ILifetimeScope scope = null);
T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class;
T Resolve<T>(params Parameter[] parameters) where T : class;
T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null);
object ResolveOptional(Type serviceType, ILifetimeScope scope = null);
object ResolveUnregistered(Type type, ILifetimeScope scope = null);
T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class;
ILifetimeScope Scope();
bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance);
}
/// <summary>
/// Container manager
/// </summary>
public class IocManager : IIocManager
{
private IContainer _container; public static IocManager Instance { get { return SingletonInstance; } }
private static readonly IocManager SingletonInstance = new IocManager(); /// <summary>
/// Ioc容器初始化
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
public IServiceProvider Initialize(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.RegisterInstance(Instance).As<IIocManager>().SingleInstance();
//所有程序集 和程序集下类型
var deps = DependencyContext.Default;
var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包
var listAllType = new List<Type>();
foreach (var lib in libs)
{
try
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
listAllType.AddRange(assembly.GetTypes().Where(type => type != null));
}
catch { }
}
//找到所有外部IDependencyRegistrar实现,调用注册
var registrarType = typeof(IDependencyRegistrar);
var arrRegistrarType = listAllType.Where(t => registrarType.IsAssignableFrom(t) && t != registrarType).ToArray();
var listRegistrarInstances = new List<IDependencyRegistrar>();
foreach (var drType in arrRegistrarType)
{
listRegistrarInstances.Add((IDependencyRegistrar)Activator.CreateInstance(drType));
}
//排序
listRegistrarInstances = listRegistrarInstances.OrderBy(t => t.Order).ToList();
foreach (var dependencyRegistrar in listRegistrarInstances)
{
dependencyRegistrar.Register(builder, listAllType);
} //注册ITransientDependency实现类
var dependencyType = typeof(ITransientDependency);
var arrDependencyType = listAllType.Where(t => dependencyType.IsAssignableFrom(t) && t != dependencyType).ToArray();
builder.RegisterTypes(arrDependencyType)
.AsImplementedInterfaces()
.InstancePerLifetimeScope()
.PropertiesAutowired().EnableInterfaceInterceptors(); foreach (Type type in arrDependencyType)
{
if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))
{
builder.RegisterType(type).As(type.BaseType)
.InstancePerLifetimeScope()
.PropertiesAutowired();
}
} //注册ISingletonDependency实现类
var singletonDependencyType = typeof(ISingletonDependency);
var arrSingletonDependencyType = listAllType.Where(t => singletonDependencyType.IsAssignableFrom(t) && t != singletonDependencyType).ToArray();
builder.RegisterTypes(arrSingletonDependencyType)
.AsImplementedInterfaces()
.SingleInstance()
.PropertiesAutowired(); foreach (Type type in arrSingletonDependencyType)
{
if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))
{
builder.RegisterType(type).As(type.BaseType)
.SingleInstance()
.PropertiesAutowired();
}
} builder.Populate(services);
_container = builder.Build();
return new AutofacServiceProvider(_container);
} /// <summary>
/// Gets a container
/// </summary>
public virtual IContainer Container
{
get
{
return _container;
}
} /// <summary>
/// Resolve
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="key">key</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Resolved service</returns>
public virtual T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
if (string.IsNullOrEmpty(key))
{
return scope.Resolve<T>();
}
return scope.ResolveKeyed<T>(key);
} /// <summary>
/// Resolve
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="key">key</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Resolved service</returns>
public virtual T Resolve<T>(params Parameter[] parameters) where T : class
{
var scope = Scope();
return scope.Resolve<T>(parameters);
} /// <summary>
/// Resolve
/// </summary>
/// <param name="type">Type</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Resolved service</returns>
public virtual object Resolve(Type type, ILifetimeScope scope = null)
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
return scope.Resolve(type);
} /// <summary>
/// Resolve all
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="key">key</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Resolved services</returns>
public virtual T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null)
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
if (string.IsNullOrEmpty(key))
{
return scope.Resolve<IEnumerable<T>>().ToArray();
}
return scope.ResolveKeyed<IEnumerable<T>>(key).ToArray();
} /// <summary>
/// Resolve unregistered service
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Resolved service</returns>
public virtual T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class
{
return ResolveUnregistered(typeof(T), scope) as T;
} /// <summary>
/// Resolve unregistered service
/// </summary>
/// <param name="type">Type</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Resolved service</returns>
public virtual object ResolveUnregistered(Type type, ILifetimeScope scope = null)
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
var constructors = type.GetConstructors();
foreach (var constructor in constructors)
{
try
{
var parameters = constructor.GetParameters();
var parameterInstances = new List<object>();
foreach (var parameter in parameters)
{
var service = Resolve(parameter.ParameterType, scope);
if (service == null) throw new Exception("Unknown dependency");
parameterInstances.Add(service);
}
return Activator.CreateInstance(type, parameterInstances.ToArray());
}
catch (Exception)
{ }
}
throw new Exception("No constructor was found that had all the dependencies satisfied.");
} /// <summary>
/// Try to resolve srevice
/// </summary>
/// <param name="serviceType">Type</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <param name="instance">Resolved service</param>
/// <returns>Value indicating whether service has been successfully resolved</returns>
public virtual bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance)
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
return scope.TryResolve(serviceType, out instance);
} /// <summary>
/// Check whether some service is registered (can be resolved)
/// </summary>
/// <param name="serviceType">Type</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Result</returns>
public virtual bool IsRegistered(Type serviceType, ILifetimeScope scope = null)
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
return scope.IsRegistered(serviceType);
} /// <summary>
/// Resolve optional
/// </summary>
/// <param name="serviceType">Type</param>
/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
/// <returns>Resolved service</returns>
public virtual object ResolveOptional(Type serviceType, ILifetimeScope scope = null)
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
return scope.ResolveOptional(serviceType);
} /// <summary>
/// Get current scope
/// </summary>
/// <returns>Scope</returns>
public virtual ILifetimeScope Scope()
{
try
{
//when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
return Container.BeginLifetimeScope();
}
catch (Exception)
{
//we can get an exception here if RequestLifetimeScope is already disposed
//for example, requested in or after "Application_EndRequest" handler
//but note that usually it should never happen //when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
return Container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
}
}
}
使用介绍
       public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
services.AddMvc();
return IocManager.Instance.Initialize(services);
}

特殊场景介绍

通过上面的封装后,我们可以把Controller的注册单独出来

    /// <summary>
///
/// </summary>
public class ControllerRegistrar : IDependencyRegistrar
{
/// <summary>
///
/// </summary>
public int Order
{
get
{
return ;
}
} /// <summary>
///
/// </summary>
/// <param name="builder"></param>
/// <param name="listType"></param>
public void Register(ContainerBuilder builder, List<Type> listType)
{
builder.RegisterType(typeof(LogInterceptor));
//注册Controller,实现属性注入
var IControllerType = typeof(ControllerBase);
var arrControllerType = listType.Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).ToArray();
builder.RegisterTypes(arrControllerType).PropertiesAutowired().EnableClassInterceptors();
}
}
下面介绍几种特殊使用方式
1.创建实例时给指定参数赋值
builder.RegisterType(typeof(TestDemo)).AsSelf();

public class TestDemo
{
private readonly string _name; private readonly string _sex; private readonly int _age; public TestDemo(string name, string sex, int age)
{
_name = name;
_age = age;
_sex = sex;
}
public string Sex
{
get
{
return _sex;
}
} public string Name
{
get
{
return _name;
}
} public int Age
{
get
{
return _age;
}
}
}
 使用示例

var iocManager = app.ApplicationServices.GetService<IIocManager>();
List<Parameter> cparams = new List<Parameter>();
cparams.Add(new NamedParameter("name", "张三"));
cparams.Add(new NamedParameter("sex", "男"));
cparams.Add(new TypedParameter(typeof(int), ));
var testDemo = iocManager.Resolve<TestDemo>(cparams.ToArray());
Console.WriteLine($"姓名:{testDemo.Name},年龄:{testDemo.Age},性别:{testDemo.Sex}");

2.对象激活事件

 Autofac暴露五个事件接口供实例的按如下顺序调用

  1. OnRegistered
  2. OnPreparing
  3. OnActivated
  4. OnActivating
  5. OnRelease

 这些事件会在注册的时候被订阅,或者被附加到IComponentRegistration 的时候。

  builder.RegisterType(typeof(TestDemo)).AsSelf()
.OnRegistered(e => Console.WriteLine("OnRegistered在注册的时候调用!"))
.OnPreparing(e => Console.WriteLine("OnPreparing在准备创建的时候调用!"))
.OnActivating(e => Console.WriteLine("OnActivating在创建之前调用!"))
.OnActivated(e => Console.WriteLine("OnActivated创建之后调用!"))
.OnRelease(e => Console.WriteLine("OnRelease在释放占用的资源之前调用!"));
可以在这些事件里面做些特殊场景处理

总结

本篇介绍了Autofac在项目中的使用方式以及几种特殊使用场景。其它未介绍知识如生命周期请参考http://autofac.readthedocs.io/en/latest/getting-started/index.html

  下面给出本文示例代码:Ywdsoft.AutofacTest

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

如果,想给予我更多的鼓励,求打

因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是焰尾迭 。

.net core2.0下Ioc容器Autofac使用的更多相关文章

  1. IoC容器Autofac - Autofac + Asp.net MVC + EF Code First(转载)

    转载地址:http://www.cnblogs.com/JustRun1983/archive/2013/03/28/2981645.html  有修改 Autofac通过Controller默认构造 ...

  2. Ioc容器Autofac系列(1)-- 初窥

     一.前言 第一次接触Autofac是因为CMS系统--Orchard,后来在一个开源爬虫系统--NCrawler中也碰到过,随着深入了解,我越发觉得Ioc容器是Web开发中必不可少的利器.那么,Io ...

  3. IoC容器Autofac学习笔记

    一.一个没有使用IoC的例子 IoC的全称是Inversion of Control,中文叫控制反转.要理解控制反转,可以看看非控制反转的一个例子. public class MPGMovieList ...

  4. [转]Ioc容器Autofac

    本文转自:http://www.cnblogs.com/hkncd/archive/2012/11/21/2780041.html Ioc容器Autofac系列(1)-- 初窥   前言 第一次接触A ...

  5. IoC容器Autofac(5) - Autofac在Asp.net MVC Filter中的应用

    Autofac结合EF在MVC中的使用,上一篇IoC容器Autofac(4) - Autofact + Asp.net MVC + EF Code First(附源码)已经介绍了.但是只是MVC中Co ...

  6. .net core2.0下使用Identity改用dapper存储数据

    前言. 已经好多天没写博客了,鉴于空闲无聊之时又兴起想写写博客,也当是给自己做个笔记.过了这么些天,我的文笔还是依然那么烂就请多多谅解了.今天主要是分享一下在使用.net core2.0下的实际遇到的 ...

  7. Net Core2.0下使用Dapper

    Net Core2.0下使用Dapper 今天成功把.Net Framework下使用Dapper进行封装的ORM成功迁移到.Net Core 2.0上,在迁移的过程中也遇到一些很有意思的问题,值得和 ...

  8. [转载]Spring下IOC容器和DI(依赖注入) @Bean及@Autowired

    Spring下IOC容器和DI(依赖注入) @Bean及@Autowired自动装配 bean是什么 bean在spring中可以理解为一个对象.理解这个对象需要换一种角度,即可将spring看做一门 ...

  9. 用Rider写一个有IOC容器Autofac的.net core的程序

    一:Autofac是一个和Java里的Spring IOC容器一样的东西,不过它确实没有Spring里的那么方便,主要是在于它没有提供足够的Api和扫描方式等等,不过优点是它比Spring要快很多,而 ...

随机推荐

  1. 错误代码: 1247 Reference 'startTime' not supported (forward reference in item list)

    1.错误描述 1 queries executed, 0 success, 1 errors, 0 warnings 查询:SELECT a.createUserId AS typeId, (SELE ...

  2. freemarker报错之九

    1.错误描述 五月 30, 2014 11:52:04 下午 freemarker.log.JDK14LoggerFactory$JDK14Logger error 严重: Template proc ...

  3. Linux显示cat帮助信息并退出

    Linux显示cat帮助信息并退出 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ cat --help 用法:cat [选项]... [文件]... 将[文件 ...

  4. CF374 Maxim and Array

    贪心 如果有0先变成非0 如果负数的个数 应该变为偶数 之后就是每次将绝对值最小的值加K #include<bits/stdc++.h> using namespace std; cons ...

  5. ASP.NET WebAPI String 传值问题

    如果我们再WebAPI中定义了只有一个string参数的WebAPI函数,如下所示: [HttpPost] public string TrackBill(string str) { return s ...

  6. Struts2的数据封装

    在很多的实际开发场景中,页面提交请求参数Action ,在Action中接收参数并对接收的数据进行封装.封装到一个JavaBean中,将JavaBean传递给业务层中.Struts2数据封装分为两类: ...

  7. Css多列语法笔记

    columns多列属性定义: column-width:规定列的宽度(此宽度是缩放scale以后的最小宽度): column-count:规定元素被分隔的列数: column-rule设置所有colu ...

  8. 2014NOIP普及组 子矩阵

    觉得题目水的离开 觉得普及组垃圾的请离开 不知道 DFS 和 DP 的请离开 不屑的大佬请离开 ……. 感谢您贡献的访问量 ————————————————华丽的分割线 ——————————————— ...

  9. 版本控制-Git对象

    Git对象 版本控制在于文件的控制,git的控制方法在于为每个文件生成(key,object)的结构.git利用sha-1加密算法,对每一个文件生成一个唯一的字符序列(明文大小不超过2^64位,对于普 ...

  10. 关于CoordinatorLayout的用法——复杂交互的克星

    好久没有写博客了,主要还是任务过多哈.在开发的过程当中,也记录了很多东西,但是技术这个事吧,其实,时效性真的事非常强--就比如说,你昨天还津津乐道的一个难点解决方案,你过个几天再回过头去看它,就会有一 ...