大数跨境
0
0

高效 WinForm 应用的秘密,合理使用缓存机制

高效 WinForm 应用的秘密,合理使用缓存机制 DotNet技术匠
2025-08-26
0
导读:Winform里面的缓存使用。

前言

在WinForm应用程序开发中,性能优化与资源高效利用始终是开发者关注的重点。缓存作为提升系统响应速度、降低数据库负载的关键技术,在中大型项目中扮演着不可或缺的角色。

本文聚焦于WinForm环境下的缓存实践,从基础的Hashtable自定义缓存到.NET 4.0引入的MemoryCache,结合实际应用场景,深入浅出地介绍如何在桌面应用中合理使用缓存机制,帮助大家开发更高效、更稳定的WinForm程序。

正文

缓存在很多情况下需要用到,合理利用缓存可以一方面可以提高程序的响应速度,同时可以减少对特定资源访问的压力。本文主要针对自己在Winform方面的缓存使用做一个引导性的介绍,希望大家能够从中了解一些缓存的使用场景和使用方法。缓存是一个中大型系统所必须考虑的问题。为了避免每次请求都去访问后台的资源(例如数据库),我们一般会考虑将一些更新不是很频繁的,可以重用的数据,通过一定的方式临时地保存起来,后续的请求根据情况可以直接访问这些保存起来的数据。这种机制就是所谓的缓存机制。

.NET 4.0的缓存功能主要由三部分组成:System.Runtime.Caching,System.Web.Caching.Cache和Output Cache。

System.Runtime.Caching这是在.NET 4.0中新增的缓存框架,主要是使用MemoryCache对象,该对象存在于程序集System.Runtime.Caching.dll。

System.Web.Caching.Cache这个则是在.NET2.0开始就一直存在的缓存对象,一般主要用在Web中,当然也可以用于Winform里面,不过要引用System.Web.dll。

Output Cache则是Asp.NET里面使用的,在ASP.NET 4.0之前的版本都是直接使用System.Web.Caching.Cache来缓存HTML片段。在ASP.NET 4.0中对它进行了重新设计,提供了一个OutputCacheProvider供开发人员进行扩展,但是它默认情况下,仍然使用System.Web.Caching.Cache来做做缓存。

1、自定义Hastable的缓存处理。

除了上面三种的缓存机制,一般我们还可以在静态对象里面通过HashTable或者Dictionary的方式进行自定义的缓存存储和使用。

例如我在我自己所开发的程序里面,都使用了工厂类来创建业务对象,由于创建业务对象以及数据访问层对象,是一个在界面或者中间层反复调用的操作,因此需要把经常调用的对象把它存储起来,下载调用的时候,直接从内存中取出来即可。如下面的BLLFactory类,就是一个基于泛型对象的业务类的创建操作,使用了基于Hashtable的静态对象进行缓存处理。

/// <summary>
/// 对业务类进行构造的工厂类
/// </summary>
/// <typeparam name="T">业务对象类型</typeparam>
publicclassBLLFactory<TwhereT : class
{
    privatestatic Hashtable objCache = new Hashtable();
    privatestaticobject syncRoot = new Object();

    /// <summary>
    /// 创建或者从缓存中获取对应业务类的实例
    /// </summary>
    publicstatic T Instance
    {
        get
        {
            string CacheKey = typeof(T).FullName;
            T bll = (T)objCache[CacheKey];  //从缓存读取  
            if (bll == null)
            {
                lock (syncRoot)
                {
                    if (bll == null)
                    {
                        bll = Reflect<T>.Create(typeof(T).FullName, typeof(T).Assembly.GetName().Name); //反射创建,并缓存
                        objCache.Add(typeof(T).FullName, bll);
                    }
                }
            }
            return bll;
        }
    }

2、使用.NET4.0的MemoryCache对象实现缓存

MemoryCache的使用网上介绍的不多,不过这个是.NET4.0新引入的缓存对象,估计主要是替换原来企业库的缓存模块,使得.NET的缓存可以无处不在,而不用基于特定的Windows版本上使用。

首先我们使用来创建一个基于MemoryCache的辅助类MemoryCacheHelper,方便调用进行缓存处理。

/// <summary>
/// 基于MemoryCache的缓存辅助类
/// </summary>
publicstaticclassMemoryCacheHelper
{
    privatestaticreadonly Object _locker = newobject();

    publicstatic T GetCacheItem<T>(String key, Func<T> cachePopulate, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null)
    {
        if(String.IsNullOrWhiteSpace(key)) thrownew ArgumentException("Invalid cache key");
        if(cachePopulate == nullthrownew ArgumentNullException("cachePopulate");
        if(slidingExpiration == null && absoluteExpiration == nullthrownew ArgumentException("Either a sliding expiration or absolute must be provided");

        if(MemoryCache.Default[key] == null)
        {
            lock(_locker)
            {
                if(MemoryCache.Default[key] == null)
                {
                    var item = new CacheItem(key, cachePopulate());
                    var policy = CreatePolicy(slidingExpiration, absoluteExpiration);

                    MemoryCache.Default.Add(item, policy);
                }
            }
        }

        return (T)MemoryCache.Default[key];
    }

    private static CacheItemPolicy CreatePolicy(TimeSpan? slidingExpiration, DateTime? absoluteExpiration)
    {
        var policy = new CacheItemPolicy();

        if(absoluteExpiration.HasValue)
        {
            policy.AbsoluteExpiration = absoluteExpiration.Value;
        }
        elseif(slidingExpiration.HasValue)
        {
            policy.SlidingExpiration = slidingExpiration.Value;
        }

        policy.Priority = CacheItemPriority.Default;

        return policy;
    }
}

这个辅助类只有一个public方法,就是GetCacheItem,使用的时候,需要指定key和获取数据的处理代理,还有缓存的过期时间,是基于TimeSpan的还是基于绝对时间的,选择其一。

上面的辅助类,我们在什么情况下会使用到呢?

假如在一个工作流模块中用到了人员ID,而人员ID需要进行人员名称的转义,人员信息我们一般知道放在权限系统模块里面,那么如果在工作流里面需要频繁对人员ID进行转义,那么就需要方法调用权限系统的接口模块,这样处理就可以使用缓存模块进行优化处理的了。

void gridView1_CustomColumnDisplayText(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e)
{
    if (e.Column.FieldName.Equals("ProcUser") || e.Column.FieldName.Equals("ProcUid") || e.Column.FieldName.Equals("UserId"))
    {
        if (e.Value != null)
        {
            e.DisplayText = SecurityHelper.GetUserFullName(e.Value.ToString());
        }
    }
}

其中的SecurityHelper.GetUserFullName是我对调用进行基于缓存的二次封装,具体逻辑如下所示。

/// <summary>
/// 根据用户的ID,获取用户的全名,并放到缓存里面
/// </summary>
/// <param name="userId">用户的ID</param>
/// <returns></returns>
public static string GetUserFullName(string userId)
{            
    string key = "Security_UserFullName" + userId;
    string fullName = MemoryCacheHelper.GetCacheItem<string>(key,
        delegate() { return BLLFactory<User>.Instance.GetFullNameByID(userId.ToInt32()); },
        new TimeSpan(0300));//30分钟过期
    return fullName;
}

MemoryCacheHelper的方法GetCacheItem里面的Func< T>我使用了一个匿名函数用来获取缓存的值。

delegate() { return BLLFactory<User>.Instance.GetFullNameByID(userId.ToInt32()); }

而调用BLLFactory< User>.Instance.GetFullNameByID则是从数据库里面获取对应的数据了。

这样在第一次或者缓存过期的时候,自动调用业务对象类的方法来获取数据了。

最后,在界面上调用GetUserFullName的方法即可实现基于缓存方式的调用,程序第一次使用的,碰到指定的键没有数据,就去数据库里面获取,以后碰到该键,则直接获取缓存的数据了。

下面图形是程序具体的实现效果。

当然,以上两种方式都还可以通过AOP的注入方式实现代码的简化操作,不过由于对AOP的引入,会涉及到更多的知识点,而且熟悉程序还不够,所以依然采用较为常用的方式来处理缓存的数据。

关键词

#WinForm#缓存#MemoryCache#Hashtable#性能优化

最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。也可以加入微信公众号[DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

作者:伍华聪

出处:cnblogs.com/wuhuacong/p/3526335.html
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!



END



方便大家交流、资源共享和共同成长
纯技术交流群,需要加入的小伙伴请扫码,并备注加群



推荐阅读




C# 部署 Yolov8 全攻略:OpenVINO 与 TensorRT  双引擎加速
WPF 一款通用的嵌入式测控上位机(灵活配置免重复)
开源工业级 WPF 数据大屏:基于 MVVM+ HandyControl 的实战项目
C# + Halcon 的模块化工业视觉通用图像处理工具(实用、高效、全场景覆盖)
开源工业级 WPF 数据大屏:基于 MVVM+ HandyControl 的实战项目
C# 的未来是 Avalonia 吗?
C# 开发桌面应用,如何做出漂亮的界面?
免费开源!基于 WPF + Scrcpy 的简洁易用 Android 投屏工具
手写 WinForm 图表控件:无依赖,支持曲线、饼图、点图和对象折线图等
企业级桌面架构如何选,MFC、WinForm 还是 WPF?一文说清
WinForm 控件自动缩放算法:简单实现自适应窗口大小
WPF 和 WinForm:.NET平台上的未来之战,谁能胜出?(投票)
干货推荐:五款功能强大的 .NET 开源工作流系统,拿来即用
全栈 .NET 低代码引擎:权限、工作流、API动态生成,开源即用
一款基于 .NET 的轻量级 ERP 进销存系统:扫码入库、订单变标签,直达发货
.NET 8 + Vue 3 的智能工厂 MES 快速开发框架:设备监控、数据大屏全覆盖
C# + OpenCvSharp 实现低成本开源单目结构光三维扫描
.NET 9 + React 基于 DDD架构的动态路由 + RBAC权限实战
基于 SunnyUI 的企业级 WinForm 快速开发框架,开箱即用!
免硬件方案!基于.NET 的摄像头扫码工具(支持回车/连续扫描)
工业级 MES 系统开发 WPF + MVVM 从入门到实战(全源码/收藏版)

C# 工业常用的控件库

C# 轻松搞定工业上位机程序开机自启

C# 工业视觉开发选择 Halcon 还是 OpenCV?

C# 上位机开发怎么学?给自动化工程师的建议

.NET 桌面应用 (WPF/WinForm) 高效自动更新解决方案

一行代码快速开发 AntdUI 风格的 WinForm 通用后台框架

WinForm + SQL Server + Modbus 实现仓库温控上位机系统开发

WinForm 开发的多功能工具:串口通信、加密解密、图像转换等功能

.NET 开源免费、功能强大的图表库 ScottPlot(WinForm/WPF 通用)

C#+ OpenCvSharp 工业视觉常用图像处理示例集(开箱即用,附源码)


觉得有收获?不妨分享让更多人受益

关注「DotNet技术匠」,共同提升技术实力


收藏
点赞
分享
在看

【声明】内容源于网络
0
0
DotNet技术匠
「DotNet技术匠」聚焦.NET核心,分享深度干货、实战技巧、最新资讯、优质资源,助你领跑技术赛道,赋能开发者成长。
内容 1715
粉丝 0
DotNet技术匠 「DotNet技术匠」聚焦.NET核心,分享深度干货、实战技巧、最新资讯、优质资源,助你领跑技术赛道,赋能开发者成长。
总阅读430
粉丝0
内容1.7k