में अभिव्यक्ति वृक्ष के साथ फ़ील्ड गेटर/सेटर this post और its follow-up question पर उदाहरणों के बाद, मैं संकलित अभिव्यक्तियों का उपयोग करके फ़ील्ड गेटर्स/सेटर्स बनाने की कोशिश कर रहा हूं।बेस क्लास
गेटर बहुत अच्छा काम करता है, लेकिन मैं सेटटर फंस गया हूं, क्योंकि मुझे किसी भी प्रकार के फ़ील्ड को असाइन करने के लिए सेटटर की आवश्यकता है।
यहाँ मेरी सेटर कार्रवाई बिल्डर:
public static Action<T1, T2> GetFieldSetter<T1, T2>(this FieldInfo fieldInfo) {
if (typeof(T1) != fieldInfo.DeclaringType && !typeof(T1).IsSubclassOf(fieldInfo.DeclaringType)) {
throw new ArgumentException();
}
ParameterExpression targetExp = Expression.Parameter(typeof(T1), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(T2), "value");
//
// Expression.Property can be used here as well
MemberExpression fieldExp = Expression.Field(targetExp, fieldInfo);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);
//
return Expression.Lambda<Action<T1, T2>> (assignExp, targetExp, valueExp).Compile();
}
अब, मैं एक कैश सूची में सामान्य setters (क्योंकि निश्चित रूप से, हर बार सेटर के निर्माण के लिए एक प्रदर्शन हत्यारा है) की दुकान है, जहां मैं उन्हें के रूप में कास्ट सरल "वस्तुओं":
void setFieldValue(string fieldName, object value) {
var setterAction = setters[fieldName];
// TODO: now the problem => how do I invoke "setterAction" with
// object and fldInfos[fieldName] as parameters...?
}
मैं:
// initialization of the setters dictionary
Dictionary<string, object> setters = new Dictionary(string, object)();
Dictionary<string, FieldInfo> fldInfos = new Dictionary(string, FieldInfo)();
FieldInfo f = this.GetType().GetField("my_int_field");
setters.Add(f.Name, GetFieldSetter<object, int>(f);
fldInfos.Add(f.Name, f);
//
f = this.GetType().GetField("my_string_field");
setters.Add(f.Name, GetFieldSetter<object, string>(f);
fldInfos.Add(f.Name, f);
अब मैं इस तरह एक फ़ील्ड मान सेट करने का प्रयास बस एक सामान्य विधि को कॉल कर सकते हैं और हर बार कास्ट कर सकते हैं, लेकिन मैं प्रदर्शन ओवरहेड के बारे में चिंतित हूं ... कोई सुझाव?
- संपादित उत्तर पर Mr Anderson's answer आधार पर, मैं एक छोटे से परीक्षण कार्यक्रम जो सीधे मान सेट की तुलना बनाया, कैश की गई प्रतिबिंब (जहां FieldInfo के कैश किए गए हैं) और कैश की गई बहु प्रकार कोड। मैं ऑब्जेक्ट विरासत का उपयोग 3 स्तर तक विरासत (ObjectC : ObjectB : ObjectA
) के साथ करता हूं।
Full code is of the example can be found here.
परीक्षण की एकल यात्रा आउटपुट निम्नलिखित देता है:
-------------------------
--- OBJECT A ---
-------------------------
Set direct: 0.0036 ms
Set reflection: 2.319 ms
Set ref.Emit: 1.8186 ms
Set Accessor: 4.3622 ms
-------------------------
--- OBJECT B ---
-------------------------
Set direct: 0.0004 ms
Set reflection: 0.1179 ms
Set ref.Emit: 1.2197 ms
Set Accessor: 2.8819 ms
-------------------------
--- OBJECT C ---
-------------------------
Set direct: 0.0024 ms
Set reflection: 0.1106 ms
Set ref.Emit: 1.1577 ms
Set Accessor: 2.9451 ms
बेशक
, यह बस से पता चलता वस्तुओं बनाने की लागत - यह हमें मापने के लिए संचित संस्करण बनाने की भरपाई की अनुमति देता है प्रतिबिंब और अभिव्यक्तियों का।
-------------------------
--- OBJECT A ---
-------------------------
Set direct: 33.2744 ms
Set reflection: 1259.9551 ms
Set ref.Emit: 531.0168 ms
Set Accessor: 505.5682 ms
-------------------------
--- OBJECT B ---
-------------------------
Set direct: 38.7921 ms
Set reflection: 2584.2972 ms
Set ref.Emit: 971.773 ms
Set Accessor: 901.7656 ms
-------------------------
--- OBJECT C ---
-------------------------
Set direct: 40.3942 ms
Set reflection: 3796.3436 ms
Set ref.Emit: 1510.1819 ms
Set Accessor: 1469.4459 ms
पूर्णता के लिए के लिए::
आगे, 1.000.000 बार चलाते हैं मैं "सेट" विधि करने के लिए कॉल हटाया प्रतिबिंब विधि के लिए सेटर (FieldInfo
हो रही की लागत को उजागर करने के अभिव्यक्ति मामले के लिए Action<object, object>
)। यहाँ के परिणाम:
-------------------------
--- OBJECT A ---
-------------------------
Set direct: 3.6849 ms
Set reflection: 44.5447 ms
Set ref.Emit: 47.1925 ms
Set Accessor: 49.2954 ms
-------------------------
--- OBJECT B ---
-------------------------
Set direct: 4.1016 ms
Set reflection: 76.6444 ms
Set ref.Emit: 79.4697 ms
Set Accessor: 83.3695 ms
-------------------------
--- OBJECT C ---
-------------------------
Set direct: 4.2907 ms
Set reflection: 128.5679 ms
Set ref.Emit: 126.6639 ms
Set Accessor: 132.5919 ms
नोट: समय यहाँ वृद्धि तथ्य यह है कि पहुँच गुना बड़ा शब्दकोशों के लिए धीमी है (के रूप में वे O(1)
अभिगम समय है) की वजह से नहीं है, लेकिन इस तथ्य की वजह है कि समय की संख्या हम इसका उपयोग बढ़ गया है (ObjectA
के लिए प्रति बार 4 बार, ObjectB
के लिए 8, ObjectC
के लिए 12) ... जैसा कि कोई देखता है, केवल सृजन ऑफ़सेट केवल एक अंतर बनाता है (जिसे उम्मीद की जा सकती है)।
नीचे पंक्ति: हमने 2 या उससे अधिक के कारक द्वारा प्रदर्शन में सुधार किया है, लेकिन हम अभी भी प्रत्यक्ष क्षेत्र सेट के प्रदर्शन से बहुत दूर हैं ... सूची में सही सेटटर को पुनर्प्राप्त करना समय के 10% ।
मैं प्रतिबिंब के स्थान पर अभिव्यक्ति पेड़ों के साथ प्रयास करूंगा। यह देखने के लिए कि क्या हम अंतराल को और कम कर सकते हैं ... कोई टिप्पणी स्वागत से अधिक है।
संपादित 2 मैं के रूप में this post पर Eli Arbel ने सुझाव दिया एक सामान्य "Accessor" वर्ग का उपयोग कर दृष्टिकोण का उपयोग कर परिणाम गयी।
"चिंतित प्रदर्शन के बारे में" काफी इसे काट नहीं है। इसका परीक्षण करें, देखें कि यह काफी अच्छा प्रदर्शन करता है, और उस पर आधारित निर्णय लेता है। मुझे कोई कारण नहीं दिख रहा है कि एक सामान्य विधि का उपयोग क्यों करना आपके वर्तमान दृष्टिकोण से भी बदतर होगा। – Luaan
मैं इस दृष्टिकोण (अभिव्यक्ति) का उपयोग करता था, फिर मैंने पाया कि 'System.Reflection.Emit.DyanamicMethod' अधिक सरल है। –
मुझे लगता है कि गतिशील रनटाइम कैश भी इस तरह की चीजें हैं। मुझे नहीं लगता कि यह इतना धीमा प्रदर्शन करेगा। – MBoros