在C#中動態訪問對象屬性時,用表達式樹可以獲得高效性能

在C#中如何用表達式樹動態訪問對象屬性的問題。用戶可能已經知道反射的基本用法,但想用表達式樹來提高性能,因為表達式樹編譯后的委托執行速度比反射快。
首先,表達式樹的基本概念。表達式樹允許在運行時構建代碼,并編譯成可執行的委托,這樣避免了反射帶來的性能開銷。用戶需要一個例子,所以我要構造一個簡單的類,比如Person,然后動態獲取它的屬性,比如Name。
接下來,需要考慮如何構建表達式樹。訪問屬性通常涉及參數表達式、屬性訪問表達式,以及可能的轉換(如果屬性是值類型需要裝箱)。步驟大致是:創建參數表達式,生成屬性訪問表達式,然后轉換成Lambda表達式,最后編譯成委托。
然后,需要處理泛型方法,因為用戶可能希望這個方法適用于任何對象和屬性類型。所以方法應該是泛型的,比如GetPropertyAccessor<T, TProp>,這樣類型安全,同時減少裝箱拆箱。
還要考慮錯誤處理,比如屬性是否存在,但用戶可能暫時不需要,例子中可以忽略,但實際使用時可能需要添加。
然后,編寫示例代碼,包括Person類,創建表達式樹,編譯成委托,并演示如何使用。用戶可能還需要比較性能,所以可以提到與反射的性能對比,但例子中可以不實現。
最后,檢查代碼是否正確,比如Expression.Property的正確使用,Lambda表達式的參數是否正確,以及編譯后的委托是否正確調用。
可能遇到的坑:值類型屬性需要轉換為object,所以可能需要Expression.Convert。例如,如果屬性是int類型,直接返回object需要轉換,否則會出錯。但如果是泛型方法返回TProp,可能不需要,但如果是返回object,則需要處理。
在用戶例子中,返回的是object,所以可能需要Convert表達式。但用戶提供的示例代碼可能沒有處理,需要檢查是否正確。比如,如果TProp是值類型,訪問時需要轉換為object,否則委托返回的是值類型,導致裝箱問題。因此,應該在表達式樹中添加Convert。
不過,在用戶提供的示例代碼中,GetPropertyAccessor返回Func<object, object>,所以當TProp是值類型時,Expression.Property會返回值類型,需要轉換為object。因此,在表達式樹中應該用Expression.Convert將屬性表達式轉換為object類型。
例如:
Expression.Property(param, propertyInfo) 是TProp類型,如果TProp是值類型,直接賦值給object會裝箱,所以需要:
Expression.Convert(Expression.Property(param, propertyInfo), typeof(object))
這樣,生成的lambda表達式就是Expression<Func<object, object>>,正確處理值類型和引用類型。
但用戶提供的示例代碼中可能沒有這一步,需要檢查。比如,原代碼中的表達式是:
Expression.Lambda<Func<object, object>>(propertyAccess, param);
而propertyAccess是Expression.Property(param, propertyInfo),此時如果propertyInfo是值類型,則propertyAccess的類型是值類型,無法直接轉換為object,所以需要Convert。
因此,正確的代碼應該在獲取屬性后,添加一個Convert表達式。所以,修改后的表達式應該是:
var propertyAccess = Expression.Property(param, propertyInfo);
if (propertyAccess.Type.IsValueType)
{
propertyAccess = Expression.Convert(propertyAccess, typeof(object));
}
或者更簡單的方式,不管是否是值類型,都轉換為object:
propertyAccess = Expression.Convert(propertyAccess, typeof(object));
但這樣可能對于引用類型會多一次轉換,不過不影響結果。因此,在構建表達式樹時,應該添加Convert。
所以,用戶提供的示例代碼可能需要調整,添加Convert步驟,否則當屬性是值類型時會出錯。
總結,正確的表達式樹構建步驟應該是:

  1. 創建參數表達式:ParameterExpression param = Expression.Parameter(typeof(object), "x");
  2. 轉換參數為具體類型:UnaryExpression convertedParam = Expression.Convert(param, typeof(T));
  3. 訪問屬性:MemberExpression propertyAccess = Expression.Property(convertedParam, propertyInfo);
  4. 將屬性值轉換為object(如果需要):UnaryExpression convertedProperty = Expression.Convert(propertyAccess, typeof(object));
  5. 構建Lambda表達式:var lambda = Expression.Lambda<Func<object, object>>(convertedProperty, param);
    但用戶希望的是通用的方法,所以可能需要泛型方法,或者正確處理類型轉換。
    可能用戶提供的例子中沒有處理這一點,導致值類型屬性無法正確返回。因此,在編寫示例時,需要注意添加Convert。
    現在,我需要將這些思考整理成一個清晰的示例,確保代碼正確,并解釋每一步的作用,同時指出可能的注意事項,比如性能優勢和類型轉換的問題。
using System;
using System.Linq.Expressions;
using System.Reflection;public class Person
{public string Name { get; set; }
}public static class PropertyAccessor
{// 創建泛型委托緩存(提升性能)private static readonly Dictionary<string, Delegate> _accessorCache = new Dictionary<string, Delegate>();public static Func<object, object> GetPropertyAccessor(Type objectType, string propertyName){var key = $"{objectType.FullName}.{propertyName}";if (!_accessorCache.TryGetValue(key, out var accessor)){// 獲取屬性信息var propertyInfo = objectType.GetProperty(propertyName);if (propertyInfo == null) throw new ArgumentException("Property not found");// 構建表達式樹var param = Expression.Parameter(typeof(object), "obj");var convertedParam = Expression.Convert(param, objectType); // 轉換輸入對象為具體類型var propertyAccess = Expression.Property(convertedParam, propertyInfo);var convertResult = Expression.Convert(propertyAccess, typeof(object)); // 處理值類型裝箱var lambda = Expression.Lambda<Func<object, object>>(convertResult, param);accessor = lambda.Compile();_accessorCache[key] = accessor;}return (Func<object, object>)accessor;}
}// 使用示例
var person = new Person { Name = "Alice" };
var accessor = PropertyAccessor.GetPropertyAccessor(typeof(Person), "Name");
var name = accessor(person); // 返回 "Alice"

注意事項

  1. 線程安全:緩存使用需考慮線程同步
  2. 內存管理:長期運行的應用程序需注意緩存大小
  3. 類型兼容:確保輸入對象可轉換為目標類型

如果需要支持更復雜的場景(如嵌套屬性訪問、索引器等),可以通過組合多個表達式節點來實現更復雜的訪問邏輯。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/70129.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70129.shtml
英文地址,請注明出處:http://en.pswp.cn/web/70129.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深入解析 Flutter 性能優化:從原理到實踐

深入解析 Flutter 性能優化&#xff1a;從原理到實踐的全面指南 Flutter 是一個高性能的跨平臺框架&#xff0c;但在開發復雜應用時&#xff0c;性能問題仍然可能出現。性能優化是開發高質量 Flutter 應用的關鍵。本篇博客將從 Flutter 的渲染原理出發&#xff0c;結合實際場景…

使用 Python 爬蟲獲取微店快遞費用 item_fee API 接口數據

在電商運營中&#xff0c;快遞費用是影響商家利潤和用戶體驗的重要因素之一。微店作為國內知名的電商平臺&#xff0c;提供了豐富的 API 接口供開發者使用&#xff0c;其中也包括查詢商品快遞費用的接口。通過調用微店的 item_fee 接口&#xff0c;開發者可以獲取指定商品的快遞…

MySQL基本操作——包含增刪查改(環境為Ubuntu20.04,MySQL5.7.42)

1.庫的操作 1.1 創建數據庫 語法&#xff1a; 說明&#xff1a; 大寫的表示關鍵字 [] 是可選項 CHARACTER SET: 指定數據庫采用的字符集 COLLATE: 指定數據庫字符集的校驗規則 1.2 創建案例 創建一個使用utf8字符集的db1數據庫 create database db1 charsetutf8; …

Spring Boot 定時任務:輕松實現任務自動化

在現代應用開發中&#xff0c;定時任務是一個常見的需求。比如&#xff0c;我們可能需要定時清理過期數據、定時發送郵件通知等。 操作流程 開啟定時任務注解 在啟動類添加注解EnableScheduling 設置時間&#xff08;固定時間間隔&#xff09; 使用 Scheduled 注解創建定時…

七星棋牌全開源修復版源碼解析:6端兼容,200種玩法全面支持

本篇文章將詳細講解 七星棋牌修復版源碼 的 技術架構、功能實現、二次開發思路、搭建教程 等內容&#xff0c;助您快速掌握該棋牌系統的開發技巧。 1. 七星棋牌源碼概述 七星棋牌修復版源碼是一款高度自由的 開源棋牌項目&#xff0c;該版本修復了原版中的多個 系統漏洞&#…

【Rust中級教程】1.12. 生命周期(進階) Pt.2:生命周期變型、協變、不變、逆變

喜歡的話別忘了點贊、收藏加關注哦&#xff08;加關注即可閱讀全文&#xff09;&#xff0c;對接下來的教程有興趣的可以關注專欄。謝謝喵&#xff01;(&#xff65;ω&#xff65;) 這篇文章在Rust初級教程的基礎上對生命周期這一概念進行了補充&#xff0c;建議先看【Rust自…

Vue 項目登錄的基本流程

Vue 用戶登錄的基本流程包括以下6個步驟&#xff1a; 步驟&#xff1a; 1. 創建登錄表單 在前端&#xff0c;首先要創建一個登錄表單&#xff0c;用戶輸入賬號&#xff08;用戶名、郵箱、手機號等&#xff09;和密碼。 示例&#xff1a;Login.vue <template><div…

【算法】回溯算法

回溯算法 什么是回溯 人生無時不在選擇。在選擇的路口&#xff0c;你該如何抉擇 ..... 回溯&#xff1a; 是一種選優搜索法&#xff0c;又稱為試探法&#xff0c;按選優條件向前搜索&#xff0c;以達到目標。但當探索到某一步時&#xff0c;發現原先選擇并不優或達不到目標&am…

SpringAI系列 - RAG篇(三) - ETL

目錄 一、引言二、組件說明三、集成示例一、引言 接下來我們介紹ETL框架,該框架對應我們之前提到的階段1:ETL,主要負責知識的提取和管理。ETL 框架是檢索增強生成(RAG)數據處理的核心,其將原始數據源轉換為結構化向量并進行存儲,確保數據以最佳格式供 AI 模型檢索。 …

2025 docker可視化管理面板DPanel的安裝

1.什么是 DPanel &#xff1f; DPanel 是一款 Docker 可視化管理面板&#xff0c;旨在簡化 Docker 容器、鏡像和文件的管理。它提供了一系列功能&#xff0c;使用戶能夠更輕松地管理和部署 Docker 環境。 軟件特點&#xff1a; 可視化管理&#xff1a;提供直觀的用戶界面&#…

基于Python的深度學習音樂推薦系統(有配套論文)

音樂推薦系統 提供實時音樂推薦功能&#xff0c;根據用戶行為和偏好動態調整推薦內容 Python、Django、深度學習、卷積神經網絡 、算法 數據庫&#xff1a;MySQL 系統包含角色&#xff1a;管理員、用戶 管理員功能&#xff1a;用戶管理、系統設置、音樂管理、音樂推薦管理、系…

微信小程序---計劃時鐘設計與實現

微信小程序-計劃時鐘已上線,歡迎各位小伙伴的測試和使用~(微信小程序搜計劃時鐘即可使用) 在這篇博客中,我們將探討如何在微信小程序中設計和實現一個任務管理功能,該功能允許用戶添加、刪除和查看任務。任務管理系統的核心是基于日期和時間的任務管理,可以設置任務的開…

RPA-實例(UiPath )

UiPath 是一個流行的機器人流程自動化(RPA)工具,用于自動化重復性任務。以下是一個簡單的實例,展示如何使用 UiPath 自動化一個常見的任務:從 Excel 文件中讀取數據并將其輸入到網頁表單中。 實例:從 Excel 讀取數據并自動填寫網頁表單 步驟 1:準備工作 安裝 UiPath S…

華為固態電池引發的思索

華為固態電池真牛&#xff01; 超長續航&#xff1a;單次充電即可行駛3000公里 極速充電&#xff1a;五分鐘內充滿80% 極致安全&#xff1a;不可燃、不漏液 長壽命設計&#xff1a;循環壽命達10000次以上 如上是華為電池展示的優勢項&#xff0c;每一條都讓我們心動不已。…

算法分析—— 《歸并排序》

《排序數組》 題目描述&#xff1a; 給你一個整數數組 nums&#xff0c;請你將該數組升序排列。 你必須在 不使用任何內置函數 的情況下解決問題&#xff0c;時間復雜度為 O(nlog(n))&#xff0c;并且空間復雜度盡可能小。 示例 1&#xff1a; 輸入&#xff1a;nums [5,2…

UEFI Spec 學習筆記---11 - Protocols — UEFI Driver Model(1)

11.UEFI Driver Model 遵循 UEFI model 的 EFI driver 是不允許去遍歷所有的 controller 來識別需要安裝到哪個 controller 上的&#xff0c;而是通過 EFI_BOOT_SERVICES 的 ConnectController 和調用 Binding Driver 來實現&#xff1b; 具體實現如下&#xff1a; CoreConne…

10G EPON光模塊

一、10G EPON對稱光模塊 工作模式&#xff1a;上行突發接收、下行連續發射。 工作原理&#xff1a;當需要發送信號時&#xff0c;系統信號通過光模塊的電接口把信號傳送到驅動芯片&#xff0c;芯片處理后&#xff0c;驅動激光器發出調制光信號&#xff0c;經光纖發到遠端&…

整合SaToken 實現登錄功能

整合SaToken 實現登錄功能 1.整合redis 1.1添加相關依賴 // 省略...<!-- Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Redi…

Vue 項目中逐步引入 TypeScript 的類型檢查

在現有的 Vue 項目中逐步引入 TypeScript 的類型檢查 本文源于一道面試題&#xff1a;注&#xff1a;兩種問法一個意思哈&#xff01;&#xff01; 問題一&#xff1a;“ 老項目Js寫的&#xff0c;如何輕量方式享受 ts 類型&#xff1f;” 問題二&#xff1a;“如何 在現有的 …

python后端調用Deep Seek API

python后端調用Deep Seek API 需要依次下載 ●Ollama ●Deepseek R1 LLM模型 ●嵌入模型nomic-embed-text / bge-m3 ●AnythingLLM 參考教程&#xff1a; Deepseek R1打造本地化RAG知識庫:安裝部署使用詳細教程 手把手教你&#xff1a;deepseek R1基于 AnythingLLM API 調用本地…