C#-钉钉开发H5应用-事件订阅

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: C#-钉钉开发H5应用-事件订阅

前期准备工作


首先在这里我们要有一个钉钉H5应用,就长这个样子


然后就是需要一个配置一个可以公网可访问的服务器,解释以下为什么这样,因为咱们要做的这个事件订阅这个功能是钉钉访问你的回调函数,所以就需要公网可访问,因为使用的是C#语言配置的,所以直接使用的window服务器通过iis发布

总结一下,你现在手里要有两个东西才能实现这个事件订阅

1、一个钉钉的H5应用

2、一个公网可访问的回调函数(接口)

3、一定要先看一遍钉钉的文档


配置流程


第一步

进入到你的H5配置页面中,找到事件与订阅,然后把里面的两个字符串给保存下,这个是可以自动生成的。

第二步

  1. 创建一个web应用程序,

  2. 选择web API 这个程序

  3. 创建成功以后找到你的controllers这个文件夹,这个里面就是写回调函数(接口)的地方所以
  4. 添加上钉钉的解密类,这个是在你接口中会用到。
using System;
using System.Text;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Linq;
namespace dingdingsuccess
{
    /**
     * 钉钉开放平台加解密方法
     */
    public class DingTalkEncryptor
    {
        //private static readonly Charset CHARSET = Charset.forName("utf-8");
        //private static readonly Base64         base64  = new Base64();
        private byte[] aesKey;
        private String token;
        private String corpId;
        /**ask getPaddingBytes key固定长度**/
        private static readonly int AES_ENCODE_KEY_LENGTH = 43;
        /**加密随机字符串字节长度**/
        private static readonly int RANDOM_LENGTH = 16;
        /**
         * 构造函数
         * @param token            钉钉开放平台上,开发者设置的token
         * @param encodingAesKey   钉钉开放台上,开发者设置的EncodingAESKey
         * @param corpId           企业自建应用-事件订阅, 使用appKey
         *                         企业自建应用-注册回调地址, 使用corpId
         *                         第三方企业应用, 使用suiteKey
         *
         * @throws DingTalkEncryptException 执行失败,请查看该异常的错误码和具体的错误信息
         */
        public DingTalkEncryptor(String token, String encodingAesKey, String corpId)
        {
            if (null == encodingAesKey || encodingAesKey.Length != AES_ENCODE_KEY_LENGTH)
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);
            }
            this.token = token;
            this.corpId = corpId;
            aesKey = Convert.FromBase64String(encodingAesKey + "=");
        }
        /**
         * 将和钉钉开放平台同步的消息体加密,返回加密Map
         */
        public Dictionary<String, String> getEncryptedMap(String plaintext)
        {
            var time = DateTime.Now.Millisecond;
            return getEncryptedMap(plaintext, time);
        }
        /**
         * 将和钉钉开放平台同步的消息体加密,返回加密Map
         * @param plaintext     传递的消息体明文
         * @param timeStamp      时间戳
         * @param nonce           随机字符串
         * @return
         * @throws DingTalkEncryptException
         */
        public Dictionary<String, String> getEncryptedMap(String plaintext, long timeStamp)
        {
            if (null == plaintext)
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
            }
            var nonce = Utils.getRandomStr(RANDOM_LENGTH);
            if (null == nonce )
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);
            }
            String encrypt = this.encrypt(nonce, plaintext);
            String signature = getSignature(token, timeStamp.ToString(), nonce, encrypt);
            Dictionary<String, String> resultMap = new Dictionary<String, String>();
            resultMap["msg_signature"] = signature;
            resultMap["timeStamp"] = timeStamp.ToString();
            resultMap["nonce"] = nonce;
            resultMap["encrypt"] = encrypt;
            return resultMap;
        }
        /**
         * 密文解密
         * @param msgSignature     签名串
         * @param timeStamp        时间戳
         * @param nonce             随机串
         * @param encryptMsg       密文
         * @return                  解密后的原文
         * @throws DingTalkEncryptException
         */
        public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)
        {
            //校验签名
            String signature = getSignature(token, timeStamp, nonce, encryptMsg);
            if (!signature.Equals(msgSignature))
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
            }
            // 解密
            String result = decrypt(encryptMsg);
            return result;
        }
        /*
         * 对明文加密.
         * @param text 需要加密的明文
         * @return 加密后base64编码的字符串
         */
        private String encrypt(String random, String plaintext)
        {
            try
            {
                byte[] randomBytes = System.Text.Encoding.UTF8.GetBytes(random);// random.getBytes(CHARSET);
                byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plaintext);// plaintext.getBytes(CHARSET);
                byte[] lengthByte = Utils.int2Bytes(plainTextBytes.Length);
                byte[] corpidBytes = System.Text.Encoding.UTF8.GetBytes(corpId);// corpId.getBytes(CHARSET);
                //MemoryStream byteStream = new MemoryStream();
                var bytestmp = new List<byte>();
                bytestmp.AddRange(randomBytes);
                bytestmp.AddRange(lengthByte);
                bytestmp.AddRange(plainTextBytes);
                bytestmp.AddRange(corpidBytes);
                byte[] padBytes = PKCS7Padding.getPaddingBytes(bytestmp.Count);
                bytestmp.AddRange(padBytes);
                byte[] unencrypted = bytestmp.ToArray();
                RijndaelManaged rDel = new RijndaelManaged();
                rDel.Mode = CipherMode.CBC;
                rDel.Padding = PaddingMode.Zeros;
                rDel.Key = aesKey;
                rDel.IV = aesKey.ToList().Take(16).ToArray();
                ICryptoTransform cTransform = rDel.CreateEncryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(unencrypted, 0, unencrypted.Length);
                return Convert.ToBase64String(resultArray, 0, resultArray.Length);
                //Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
                //SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
                //IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
                //cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
                //byte[] encrypted = cipher.doFinal(unencrypted);
                //String result = base64.encodeToString(encrypted);
                //return result;
            }
            catch (Exception e)
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
            }
        }
        /*
         * 对密文进行解密.
         * @param text 需要解密的密文
         * @return 解密得到的明文
         */
        private String decrypt(String text)
        {
            byte[] originalArr;
            try
            {
                byte[] toEncryptArray = Convert.FromBase64String(text);
                RijndaelManaged rDel = new RijndaelManaged();
                rDel.Mode = CipherMode.CBC;
                rDel.Padding = PaddingMode.Zeros;
                rDel.Key = aesKey;
                rDel.IV = aesKey.ToList().Take(16).ToArray();
                ICryptoTransform cTransform = rDel.CreateDecryptor();
                originalArr = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                //return System.Text.UTF8Encoding.UTF8.GetString(resultArray);
                 设置解密模式为AES的CBC模式
                //Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
                //SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
                //IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
                //cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
                 使用BASE64对密文进行解码
                //byte[] encrypted = Base64.decodeBase64(text);
                 解密
                //originalArr = cipher.doFinal(encrypted);
            }
            catch (Exception e)
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
            }
            String plainText;
            String fromCorpid;
            try
            {
                // 去除补位字符
                byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
                Console.Out.WriteLine("bytes size:" + bytes.Length);
                // 分离16位随机字符串,网络字节序和corpId
                byte[] networkOrder = bytes.Skip(16).Take(4).ToArray();// Arrays.copyOfRange(bytes, 16, 20);
                for (int i = 0; i < 4; i++)
                {
                    Console.Out.WriteLine("networkOrder size:" + (int)networkOrder[i]);
                }
                //Console.Out.WriteLine("bytes plainText:" + networkOrder.Length +" "+JsonSerializer.Serialize(networkOrder));
                int plainTextLegth = Utils.bytes2int(networkOrder);
                Console.Out.WriteLine("bytes size:" + plainTextLegth);
                plainText = System.Text.UTF8Encoding.UTF8.GetString(bytes.Skip(20).Take(plainTextLegth).ToArray()); // new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
                fromCorpid = System.Text.UTF8Encoding.UTF8.GetString(bytes.Skip(20 + plainTextLegth).ToArray()); //new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);
                Console.Out.WriteLine("bytes plainText:" + plainText);
            }
            catch (Exception e)
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
            }
            Console.Out.WriteLine(fromCorpid+"====="+ corpId);
            // corpid不相同的情况
            if (!fromCorpid.Equals(corpId))
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);
            }
            return plainText;
        }
        /**
         * 数字签名
         * @param token         isv token
         * @param timestamp     时间戳
         * @param nonce          随机串
         * @param encrypt       加密文本
         * @return
         * @throws DingTalkEncryptException
         */
        public String getSignature(String token, String timestamp, String nonce, String encrypt)
        {
            try
            {
                Console.Out.WriteLine(encrypt);
                String[] array = new String[] { token, timestamp, nonce, encrypt };
                Array.Sort(array, StringComparer.Ordinal);
                //var tmparray = array.ToList();
                //tmparray.Sort(new JavaStringComper());
                //array = tmparray.ToArray();
                //Console.Out.WriteLine("array:"+JsonSerializer.Serialize(array));
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < 4; i++)
                {
                    sb.Append(array[i]);
                }
                String str = sb.ToString();
                Console.Out.WriteLine(str);
                //MessageDigest md = MessageDigest.getInstance("SHA-1");
                //md.update(str.getBytes());
                //byte[] digest = md.digest();
                System.Security.Cryptography.SHA1 hash = System.Security.Cryptography.SHA1.Create();
                System.Text.Encoding encoder = System.Text.Encoding.ASCII;
                byte[] combined = encoder.GetBytes(str);
                byte 转换
                //sbyte[] myByte = new sbyte[]
                //byte[] mySByte = new byte[myByte.Length];
                //for (int i = 0; i < myByte.Length; i++)
                //{
                //    if (myByte[i] > 127)
                //        mySByte[i] = (sbyte)(myByte[i] - 256);
                //    else
                //        mySByte[i] = (sbyte)myByte[i];
                //}
                byte[] digest = hash.ComputeHash(combined);
                StringBuilder hexstr = new StringBuilder();
                String shaHex = "";
                for (int i = 0; i < digest.Length; i++)
                {
                    shaHex = ((int)digest[i]).ToString("x");// Integer.toHexString(digest[i] & 0xFF);
                    if (shaHex.Length < 2)
                    {
                        hexstr.Append(0);
                    }
                    hexstr.Append(shaHex);
                }
                return hexstr.ToString();
            }
            catch (Exception e)
            {
                throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
            }
        }
    }
    /**
 * 钉钉开放平台加解密异常类
 */
    public class DingTalkEncryptException : Exception
    {
        /**成功**/
        public static readonly int SUCCESS = 0;
        /**加密明文文本非法**/
        public readonly static int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
        /**加密时间戳参数非法**/
        public readonly static int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
        /**加密随机字符串参数非法**/
        public readonly static int ENCRYPTION_NONCE_ILLEGAL = 900003;
        /**不合法的aeskey**/
        public readonly static int AES_KEY_ILLEGAL = 900004;
        /**签名不匹配**/
        public readonly static int SIGNATURE_NOT_MATCH = 900005;
        /**计算签名错误**/
        public readonly static int COMPUTE_SIGNATURE_ERROR = 900006;
        /**计算加密文字错误**/
        public readonly static int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
        /**计算解密文字错误**/
        public readonly static int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
        /**计算解密文字长度不匹配**/
        public readonly static int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
        /**计算解密文字corpid不匹配**/
        public readonly static int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
        private static Dictionary<int, String> msgMap = new Dictionary<int, String>();
        static DingTalkEncryptException()
        {
            msgMap[SUCCESS] = "成功";
            msgMap[ENCRYPTION_PLAINTEXT_ILLEGAL] = "加密明文文本非法";
            msgMap[ENCRYPTION_TIMESTAMP_ILLEGAL] = "加密时间戳参数非法";
            msgMap[ENCRYPTION_NONCE_ILLEGAL] = "加密随机字符串参数非法";
            msgMap[SIGNATURE_NOT_MATCH] = "签名不匹配";
            msgMap[COMPUTE_SIGNATURE_ERROR] = "签名计算失败";
            msgMap[AES_KEY_ILLEGAL] = "不合法的aes key";
            msgMap[COMPUTE_ENCRYPT_TEXT_ERROR] = "计算加密文字错误";
            msgMap[COMPUTE_DECRYPT_TEXT_ERROR] = "计算解密文字错误";
            msgMap[COMPUTE_DECRYPT_TEXT_LENGTH_ERROR] = "计算解密文字长度不匹配";
            msgMap[COMPUTE_DECRYPT_TEXT_CORPID_ERROR] = "计算解密文字corpid不匹配";
        }
        private int code;
        public DingTalkEncryptException(int exceptionCode) : base(msgMap[exceptionCode])
        {
            this.code = exceptionCode;
        }
    }
    /*
     * PKCS7算法的加密填充
     */
    public class PKCS7Padding
    {
        //private readonly static Charset CHARSET = Charset.forName("utf-8");
        private readonly static int BLOCK_SIZE = 32;
        /**
         * 填充mode字节
         * @param count
         * @return
         */
        public static byte[] getPaddingBytes(int count)
        {
            int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
            if (amountToPad == 0)
            {
                amountToPad = BLOCK_SIZE;
            }
            char padChr = chr(amountToPad);
            String tmp = string.Empty; ;
            for (int index = 0; index < amountToPad; index++)
            {
                tmp += padChr;
            }
            return System.Text.Encoding.UTF8.GetBytes(tmp);
        }
        /**
         * 移除mode填充字节
         * @param decrypted
         * @return
         */
        public static byte[] removePaddingBytes(byte[] decrypted)
        {
            int pad = (int)decrypted[decrypted.Length - 1];
            if (pad < 1 || pad > BLOCK_SIZE)
            {
                pad = 0;
            }
            //Array.Copy()
            var output = new byte[decrypted.Length - pad];
            Array.Copy(decrypted, output, decrypted.Length - pad);
            return output;
        }
        private static char chr(int a)
        {
            byte target = (byte)(a & 0xFF);
            return (char)target;
        }
    }
    /**
 * 加解密工具类
 */
    public class Utils
    {
        /**
     *
     * @return
     */
        public static String getRandomStr(int count)
        {
            String baset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            Random random = new Random();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < count; i++)
            {
                int number = random.Next(baset.Length);
                sb.Append(baset[number]);
            }
            return sb.ToString();
        }
        /*
         * int转byte数组,高位在前
         */
        public static byte[] int2Bytes(int count)
        {
            byte[] byteArr = new byte[4];
            byteArr[3] = (byte)(count & 0xFF);
            byteArr[2] = (byte)(count >> 8 & 0xFF);
            byteArr[1] = (byte)(count >> 16 & 0xFF);
            byteArr[0] = (byte)(count >> 24 & 0xFF);
            return byteArr;
        }
        /**
         * 高位在前bytes数组转int
         * @param byteArr
         * @return
         */
        public static int bytes2int(byte[] byteArr)
        {
            int count = 0;
            for (int i = 0; i < 4; ++i)
            {
                count <<= 8;
                count |= byteArr[i] & 255;
            }
            return count;
        }
    }
    public class JavaStringComper : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            return String.Compare(x,y);
        }
    }
     测试加解密的正确性
    //public class Program
    //    {
    //        public static void Main(string[] args)
    //        {
    //            String[] a = new String[] { "1", "W", "t" };
    //            var ding = new DingTalkEncryptor("tokenxxxx", "o1w0aum42yaptlz8alnhwikjd3jenzt9cb9wmzptgus", "dingxxxxxx");
    //            var msg = ding.getEncryptedMap("success");
    //            Console.Out.WriteLine(msg);
    //            //msg_signature, $data->timeStamp, $data->nonce, $data->encrypt
    //            var text = ding.getDecryptMsg(msg["msg_signature"], msg["timeStamp"], msg["nonce"], msg["encrypt"]);
    //            Console.Out.WriteLine(text);
    //            // "msg_signature":"c01beb7b06384cf416e04930aed794684aae98c1","encrypt":"","timeStamp":,"nonce":""
    //            //{"timeStamp":"1605695694141","msg_signature":"702c953056613f5c7568b79ed134a27bd2dcd8d0",
    //            //"encrypt":"","nonce":"WelUQl6bCqcBa2fMc6eI"}
    //            text = ding.getDecryptMsg("f36f4ba5337d426c7d4bca0dbcb06b3ddc1388fc", "1605695694141", "WelUQl6bCqcBa2fM", "X1VSe9cTJUMZu60d3kyLYTrBq5578ZRJtteU94wG0Q4Uk6E/wQYeJRIC0/UFW5Wkya1Ihz9oXAdLlyC9TRaqsQ==");
    //            Console.Out.WriteLine(text);
    //        }
    //    }
}

在你Controllers中的valueControllers中编写你的访问接口也可以,你重新创建一个Controllers类也可以,我们写的这个接口是需要钉钉调用的,那自然这个就口就不能是谁都可以访问了,所以就需要上面这个钉钉的加解密类类处理钉钉给我们发送过来的加密数据了。代码就是下面这种形式的

 /// <summary>
        /// 钉钉回调事件方法
        /// </summary>
        /// <param name="signature">消息体签名</param>
        /// <param name="timestamp">事件戳</param>
        /// <param name="nonce">随机字符串</param>
        /// <returns></returns>
        [HttpPost]
        public DingDingBackEntity DingDingCallBack(string signature, string timestamp, string nonce)
        {
            //这两句代码是为了接收body体中传入的加密json串
            Request.Content.ReadAsStreamAsync().Result.Seek(0, System.IO.SeekOrigin.Begin);
            string content = Request.Content.ReadAsStringAsync().Result;
            //反序列化json串拿去加密字符串
            JToken json = JToken.Parse(content);
            string ever = json["encrypt"].ToString();
            //实例化钉钉解密类构造参数为对应的 应用中的token、appkeys、AppKey值
            DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor("GbBjwX7fEWkmfo5eOWasywb", "wlMPytcxt8TzKKZolB9DIl9Io3oJunIeqe8SFtAOWib", "dingpzxdrfiyyanfidnj");
            //定义字符串接收解密后的值
            string returnDate = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, ever);
            JToken jToken = JToken.Parse(returnDate);
            //取出事件类型字段
            string EventType = jToken["EventType"].ToString();
            //这里解密以后取出的这个EventType这个参数对应的就是你所要匹配的事件了具体的事件参数类型可查看钉钉的接口文档
      //https://open.dingtalk.com/document/org/event-list-1
            //将json串中的指定值取出           
            var msg = dingTalkEncryptor.getEncryptedMap("success");
            DingDingBackEntity back = new DingDingBackEntity();
            back.msg_signature = msg["msg_signature"];
            back.encrypt = msg["encrypt"];
            back.timeStamp = msg["timeStamp"];
            back.nonce = msg["nonce"];
            // string backmsg = JsonConvert.SerializeObject(msg);
            return back;
        }
  1. 以上这些都创建好了以后就可以把你的程序通过iis发布出去了,长这个鬼样子这
    这个API进去就能看到你在value或者你重新创建的Controllers写的这个钉钉回调接口了
  2. 最后一步就是将写好的这个接口地址放入到事件订阅中的请求地址这一栏了

这里要说明一下,如果你放入了接口以后没有成功那么多半是接口这里出问题了,看看是不是接口地址填写错误了或者接口返回的参数是不是正确的json格式,还有就是接口响应时间不能超过1500ms。

如果还有不明白的地方请留言评论指证出来,我再完善。

这是钉钉配置事件订阅说明文档
/

相关文章
|
1月前
|
前端开发 JavaScript 安全
C#一分钟浅谈:Blazor WebAssembly 开发
Blazor WebAssembly 是一个客户端框架,允许开发者使用C#和Razor语法构建Web应用。本文介绍了Blazor WebAssembly的基本概念、常见问题及解决方案,包括路由配置、数据绑定、异步操作、状态管理和性能优化等方面的内容,并分享了一些易错点及如何避免的方法。希望这些内容能帮助你在Blazor WebAssembly开发中少走弯路,提高开发效率。
108 51
|
1月前
|
开发框架 缓存 .NET
C# 一分钟浅谈:Blazor Server 端开发
Blazor Server 是基于 ASP.NET Core 的框架,允许使用 C# 和 Razor 语法构建交互式 Web 应用。本文介绍 Blazor Server 的基本概念、快速入门、常见问题及解决方案,帮助开发者快速上手。涵盖创建应用、基本组件、数据绑定、状态管理、跨组件通信、错误处理和性能优化等内容。
40 1
|
1月前
|
缓存 C# 开发者
C# 一分钟浅谈:Blazor Server 端开发
本文介绍了 Blazor Server,一种基于 .NET 的 Web 开发模型,允许使用 C# 和 Razor 语法构建交互式 Web 应用。文章从基础概念、创建应用、常见问题及解决方案、易错点及避免方法等方面详细讲解,帮助开发者快速上手并提高开发效率。
53 2
|
1月前
|
测试技术 Go C#
C#一分钟浅谈:ReSharper 插件增强开发效率
【10月更文挑战第25天】ReSharper 是 JetBrains 开发的一款 Visual Studio 插件,旨在提高 .NET 开发者的生产力。它通过代码分析、重构、导航等功能,帮助开发者避免常见错误,提升代码质量和开发效率。本文将通过具体代码案例,详细介绍 ReSharper 的常见功能及其应用。
44 1
|
1月前
|
C# Python
使用wxpython开发跨平台桌面应用,对wxpython控件实现类似C#扩展函数处理的探究
【10月更文挑战第30天】使用 `wxPython` 开发跨平台桌面应用时,可以通过创建辅助类来模拟 C# 扩展函数的功能。具体步骤包括:1. 创建辅助类 `WxWidgetHelpers`;2. 在该类中定义静态方法,如 `set_button_color`;3. 在应用中调用这些方法。这种方法提高了代码的可读性和可维护性,无需修改 `wxPython` 库即可为控件添加自定义功能。但需要注意显式调用方法和避免命名冲突。
|
2月前
|
JSON C# 开发者
C#语言新特性深度剖析:提升你的.NET开发效率
【10月更文挑战第15天】C#语言凭借其强大的功能和易用性深受开发者喜爱。随着.NET平台的演进,C#不断引入新特性,如C# 7.0的模式匹配和C# 8.0的异步流,显著提升了开发效率和代码可维护性。本文将深入探讨这些新特性,助力开发者在.NET开发中更高效地利用它们。
42 1
|
2月前
|
开发框架 NoSQL MongoDB
C#/.NET/.NET Core开发实战教程集合
C#/.NET/.NET Core开发实战教程集合
|
3月前
|
物联网 C# C语言
物联网开发中C、C++和C#哪个更好用
在物联网(IoT)开发中,C、C++和C#各有优缺点,适用场景不同。C语言性能高、资源占用低,适合内存和计算能力有限的嵌入式系统,但开发复杂度高,易出错。C++支持面向对象编程,性能优秀,适用于复杂应用,但学习曲线陡峭,编译时间长。C#易于学习,与.NET框架结合紧密,适合快速开发Windows应用,但性能略低,平台支持有限。选择语言需根据具体项目需求、复杂性和团队技术栈综合考虑。
|
2月前
|
XML 存储 安全
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
101 0
|
2月前
|
人工智能 安全 机器人
Dify开发Agent对接钉钉机器人
这篇文章详细讲解了如何在Dify平台上开发一个Agent并与钉钉机器人集成,实现自动化消息处理和响应功能。
220 0

热门文章

最新文章