2011-11-14 24 views
6

के लिए अज्ञात प्रकारों की गतिशील कास्टिंग मैं एक प्रोटोकॉल के साथ इंटरऑप के लिए स्ट्रक्चर प्रकारों के लिए कस्टम सीरिएलाइज़र लिख रहा हूं, जिसे मैं बदल नहीं सकता। मैं संरचना सदस्य मूल्यों को खींचने और उन्हें BinaryWriter पर लिखने के लिए प्रतिबिंब का उपयोग कर रहा हूं। यह केवल बुनियादी प्रकारों और उनके सरणी का समर्थन करने के लिए डिज़ाइन किया गया है।सीरियलाइजेशन

if  (fi.FieldType.Name == "Int16") bw.Write((Int16)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "UInt16") bw.Write((UInt16)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "Int32") bw.Write((Int32)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "UInt32") bw.Write((UInt32)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "Int64") bw.Write((Int64)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "UInt64") bw.Write((UInt64)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "Single") bw.Write((float)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "Double") bw.Write((double)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "Decimal") bw.Write((decimal)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "Byte") bw.Write((byte)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "SByte") bw.Write((sbyte)fi.GetValue(obj)); 
else if (fi.FieldType.Name == "String") bw.Write((string)fi.GetValue(obj)); 

जाहिर है इस बदसूरत है, और यह और भी अधिक बदसूरत हो जाता है जब मैं भी इन प्रकार की सरणियों के साथ एक ही बात करना चाहता हूँ।

bw.Write((fi.FieldType) fi.GetValue(obj)); 

तो सरणियों के लिए बात की एक ऐसी ही तरह कार्य करें:

क्या वास्तव में अच्छा होगा अगर मैं कुछ इस तरह कर सकता है।

कोई भी विचार?

+0

+1, मैं थोड़ी देर के लिए ऐसा करने का एक ठोस तरीका ढूंढ रहा हूं। –

+0

फाई का प्रकार क्या है? – drdwilcox

+0

यदि बदसूरत कोड एकमात्र विकल्प बनता है, तो मैं आम तौर पर बेवकूफ गलतियों से बचने के लिए इस तरह के परिदृश्य के लिए टी 4 टेम्पलेट का उपयोग करता हूं और विजुअल स्टूडियो को स्वचालित रूप से मेरे लिए सभी कोड उत्पन्न करने देता हूं। आपको बस फिर से शुरू करने के लिए प्रकारों की एक सूची की आवश्यकता होगी या ऐसा कुछ। – mellamokb

उत्तर

4

आप मुझे तीन विकल्प के बारे में सोच सकते हैं Write

public static void WriteField(BinaryWriter bw, object obj, FieldInfo fieldInfo) 
{ 
    typeof(BinaryWriter) 
     .GetMethod("Write", new Type[] { fieldInfo.FieldType }) 
     .Invoke(bw, new object[] { fieldInfo.GetValue(obj) }); 
} 
+0

ऐसा लगता है कि यह काम कर सकता है। मैं इसे जाने दूंगा। – Polynomial

+0

@ पॉलिनोमियल नोट कि यह एक बड़ा सौदा धीमा होगा ... प्रतिबिंब कुख्यात रूप से धीमा है। आपके परिदृश्य के आधार पर यह अभी भी ** तेजी से पर्याप्त ** हो सकता है, हालांकि। –

+0

बहुत बढ़िया। बिल्कुल सही काम करता है! : डी – Polynomial

2

यह कोड वास्तव में बदसूरत नहीं है ... यह सिर्फ दोहराव है। लेकिन यह वास्तव में समझने के लिए बहुत साफ, छोटा और बहुत आसान है। यदि आपके पास एक लाख अलग-अलग प्रकार के खाते हैं, तो यह एक बात होगी, लेकिन केवल सीमित संख्या है।

यदि आप ऐसा करने में सक्षम हैं जो आप करना चाहते हैं, तो इसमें कोई समस्या होने पर इसे बनाए रखना मुश्किल होगा या इसे और कुछ करने की आवश्यकता है और दूसरा प्रोग्रामर इसे समझ नहीं सकता है ... या आप हो सकता है कि आपने जो बिल्ली किया है उसे भूल गए हैं और इसे रिलीज़ करना है।

कर यह आप होगा द्वारा: -added अतिरिक्त विकास समय -reduced पठनीयता -reduced गति -increased रखरखाव

कभी कभी हम वह भी सरल हैं समस्याओं लेते हैं और उन्हें और अधिक चुनौतीपूर्ण बनाने के लिए पसंद करते हैं। लेकिन अक्सर अच्छा व्यापार कोड सिर्फ सांसारिक, उबाऊ कोड है।

+0

"यह सिर्फ दोहराया गया" के संबंध में, यह बहुत अधिक भयानक हो जाता है जब मुझे 'टी []', 'सूची ', 'शब्दकोश ', साथ ही साथ deserialization भी खाते हैं। – Polynomial

+0

मैं समझता हूं। मुझे लगता है कि जैकब का जवाब एक बहुत सी सरल है ... यदि आप इस मार्ग को लेना चाहते हैं तो मैं वीसीएस जोन्स समाधान पर चयन करूंगा। –

2

यदि आप इसे सरल बनाना चाहते हैं, तो आप गतिशील रूप से सही कॉल करने के लिए एक अभिव्यक्ति का उपयोग कर सकते हैं।

//Cache the generated method for re-use later, say as a static field of dictionary. It shouldn't grow too-big given the number of overloads of Write. 
private static Dictionary<Type, Action<BinaryWriter, object>> _lambdaCache = new Dictionary<Type, Action<BinaryWriter, object>>(); 

//... 

if (!_lambdaCache.ContainsKey(fi.FieldType)) 
{ 
    var binaryWriterParameter = Expression.Parameter(typeof(BinaryWriter)); 
    var valueParameter = Expression.Parameter(typeof(object)); 
    var call = Expression.Call(binaryWriterParameter, "Write", null, Expression.Convert(valueParameter, fi.FieldType)); 
    var lambda = Expression.Lambda<Action<BinaryWriter, object>>(call, binaryWriterParameter, valueParameter).Compile(); 
    _lambdaCache.Add(fi.FieldType, lambda); 
} 
var write = _lambdaCache[fi.FieldType]; 
write(bw, fi.GetValue(obj)); 

क्या हम यहाँ क्या कर रहे हैं गतिशील रूप से कॉल है कि आप द्विआधारी लेखक की जरूरत बनाने के लिए कोड पैदा कर रहा है। यह इससे कहीं अधिक जटिल लगता है, लेकिन हम जो कर रहे हैं वह BinaryWriter की "लिखें" विधि में अभिव्यक्ति बना रहा है। हम Expression.Convert का उपयोग करके गतिशील रूप से इसे कास्ट करते हैं, इसलिए Write का सही अधिभार कहा जाता है। हम बाइनरीवाइटर के दो पैरामीटर और लिखने के लिए मूल्य लेते हैं। अंत में, हम लैम्ब्डा संकलित करते हैं और बाद में पुनः उपयोग के लिए उस प्रकार के लिए इसे कैश करते हैं।

आपकी ज़रूरतों के आधार पर, यह BinaryWriter पर प्रतिबिंब का उपयोग करने से बहुत तेज होगा।

+0

यदि आप यह कैसे और क्यों काम करते हैं, तो कुछ और विवरण जोड़ते हैं, तो मैं इसे ऊपर उठाऊंगा। –

+0

क्या यह 'लिखित' खाते में अधिभार लेगा? 'GetValue'' ऑब्जेक्ट GetValue (ऑब्जेक्ट) 'की विधि हस्ताक्षर है, इस तथ्य के बावजूद कि' FI.GetValue (obj) 'लौटा हुआ प्रकार' 3232 'वापस लौटाया गया है, तो उसे' लिखें (Int32) 'कॉल करना होगा। – Polynomial

+0

इसके अलावा एक अच्छा समाधान फ़ील्ड मान को निरंतर के रूप में पास करता है और इसलिए आपको एक नया लैम्ब्डा बनाना होगा और प्रत्येक बार कक्षा/संरचना के किसी भी उदाहरण को क्रमबद्ध करने के लिए इसे संकलित करना होगा। क्या आप एक अभिव्यक्ति जोड़ सकते हैं। फ़ील्ड मान को सही प्रकार में बदलने के लिए कनवर्ट करें और फिर फ़ील्ड को पैरामीटर मान दें? फिर आप इसे फिर से संकलित किए बिना बार-बार कॉल कर सकते हैं। –

2

मैं protobuf-net के लिए कुछ बहुत ही समान कोड करता हूं; Type.GetTypeCode(...) एक वरदान, एक switch लिए अनुमति देता है:

switch(Type.GetTypeCode(fi.FieldType)) { 
    case TypeCode.Int16: bw.Write((Int16)fi.GetValue(obj)); break 
    case TypeCode.UInt32: bw.Write((UInt16)fi.GetValue(obj)); break; 
     ... etc lots and lots 
} 

अभी भी एक सा दोहराव है, लेकिन आप केवल एक बार Type को देखो - बाकी एक switch है।

आप 4.0 का उपयोग कर रहे हैं, तो एक और चाल हो सकती है:

dynamic value = fi.GetValue(obj); 
bw.Write(value); 

जो कार्यावधि में सबसे उपयुक्त अधिभार लेने की कोशिश करेंगे।हालांकि, मेरे दृश्य में, यह कारण नहीं है - dynamic का उपयोग करने के लिए पर्याप्त कारण नहीं है।

एक अंतिम सोचा होगा: कार्यावधि में कोड बनाने के लिए मेटा प्रोग्रामिंग (जैसे ILGenerator के रूप में) का उपयोग करें - और अधिक जटिल है, लेकिन तेजी से, और निष्पादन समय (कम से इन चेकों के किसी भी नहीं है जब की तैयारी आदर्श)।

1

का सही संस्करण आह्वान करने के लिए प्रतिबिंब का उपयोग कर सकते हैं:

1) BinaryFormatter - यह बहुत ही बस अपने कार्य को पूरा करने, द्वारा सक्षम हो सकता है Serialize विधि।
2) जैसा कि आप सुझाव देते हैं, प्रतिबिंब का उपयोग करते हुए। कोड इस तरह कुछ दिखाई देगा:

// sample source data 
object src = (uint)234; 

var bwType = typeof(BinaryWriter); 
var argTypes = new Type[] { src.GetType() }; 
var m = bwType.GetMethod("Write", argTypes); 
var args = new object[] { src }; 
m.Invoke(bw, args); 

3) कोड उत्पन्न करने के लिए एक टी 4 टेम्पलेट का उपयोग करें। कोड अभी भी बदसूरत है, लेकिन कम से कम बनाए रखने के लिए बहुत कम काम करता है। मैं अक्सर अपनी कुछ परियोजनाओं में इस पैटर्न का उपयोग करता हूं क्योंकि यह दोनों दुनिया के सर्वश्रेष्ठ है - प्रतिबिंब से कोई प्रदर्शन जुर्माना नहीं, लेकिन गतिशील रूप से जेनरेट किए गए कोड के सभी लाभ।

0

भले ही आप कुछ और नहीं करते हैं, switch तारों के साथ काम करता है, और यह आपके द्वारा पढ़ने के लिए बहुत आसान होगा।

को देखते हुए स्पष्ट डाली काम कर रहा है:

Type t = Type.GetType(String.Concat("System.", fi.FieldType.Name)); 

फिर

MethodInfo m = typeof(BinaryWriter).GetMethod("Write", new type[] { t }); 

का उपयोग करते हैं यह शून्य

m.Invoke(bw, new object[] { fi.GetValue(obj) }); 

यह मानते हुए है FieldType.Name एक प्रकार दायरे में है कि से मेल खाती है नहीं है। यह नहीं कहा कि एक सरणी के लिए वहां क्या होगा, लेकिन यदि यह Int16[] है, तो यह केवल थोड़ी सी चीज है, और BinaryWriter उप-वर्ग हो सकता है और बॉक्स में से किसी एक के साथ सौदा नहीं होने के लिए कुछ और अधिभार जोड़ना पड़ता है। यदि आप इनमें से बहुत कुछ कर रहे हैं, तो कुछ प्रकार के कैश Name, Type और MethodInfo, शायद उपयोगी होंगे। इस प्रश्न के लिए

संबंधित मुद्दे