एक प्रकार चर का उपयोग करके एक चर कास्टिंग


281

C # में मैं टाइप T के वेरिएबल में टाइप ऑब्जेक्ट के वेरिएबल को टाइप कर सकता हूं जहां T को टाइप वेरिएबल में परिभाषित किया गया है?


12
सख्ती से विषय पर नहीं, लेकिन आप "कास्ट" के बारे में पर्याप्त फजी लग रहे हैं इसका मतलब है कि यह ठीक से समझने का एक अच्छा विचार हो सकता है कि कास्ट ऑपरेटर के उद्देश्य और शब्दार्थ क्या हैं। यहाँ एक अच्छी शुरुआत है: blogs.msdn.com/ericlippert/archive/2009/03/19/…
एरिक

2
मुझे लगा कि मैं कुछ लेकर आया हूं। यदि आपके पास एक Typeचर है, तो आप उस प्रकार का एक उदाहरण बनाने के लिए प्रतिबिंब का उपयोग कर सकते हैं। और फिर आप उस प्रकार के पैरामीटर से इनफ़ॉर्म करके अपने इच्छित प्रकार को वापस करने के लिए एक सामान्य विधि का उपयोग कर सकते हैं। दुर्भाग्य से, किसी भी प्रतिबिंब विधि जो एक प्रकार का उदाहरण बनाता है, में एक वापसी प्रकार होगा object, इसलिए आपका सामान्य CastByExampleतरीका भी उपयोग करेगा object। तो वास्तव में ऐसा करने का कोई तरीका नहीं है, और यहां तक ​​कि अगर वहाँ था, तो आप नए कलाकारों के साथ क्या करेंगे? आप इसके तरीकों या किसी भी चीज़ का उपयोग नहीं कर सकते क्योंकि आप इसके प्रकार को नहीं जानते हैं।
काइल डेलानी

@KyleDelaney धन्यवाद, मैं पूरी तरह से सहमत हूँ! जैसा कि मैंने अपने उत्तर में समझाने की कोशिश की, यह वास्तव में उस चीज़ को अलग करने के लिए उपयोगी नहीं है, जो आपके द्वारा उपयोग किए जा रहे प्रकार को परिभाषित करने वाले कुछ बिंदुओं के बिना है। प्रकारों की पूरी बात संकलक समय प्रकार की जाँच है। यदि आपको ऑब्जेक्ट पर कॉल करने की आवश्यकता है, तो आप उपयोग कर सकते हैं objectया कर सकते हैं dynamic। यदि आप बाहरी मॉड्यूल को गतिशील रूप से लोड करना चाहते हैं, तो आपके पास कक्षाएं एक सामान्य इंटरफ़ेस साझा कर सकती हैं और उस पर ऑब्जेक्ट डाल सकती हैं। यदि आप तीसरे पक्ष के कोड को नियंत्रित नहीं करते हैं, तो छोटे रैपर बनाएं और उस पर इंटरफ़ेस लागू करें।
ज्येफ्राक्स

जवाबों:


203

यहाँ कास्ट और कन्वर्ट का एक उदाहरण है:

using System;

public T CastObject<T>(object input) {   
    return (T) input;   
}

public T ConvertObject<T>(object input) {
    return (T) Convert.ChangeType(input, typeof(T));
}

संपादित करें:

टिप्पणियों में कुछ लोगों का कहना है कि यह जवाब सवाल का जवाब नहीं देता है। लेकिन रेखा (T) Convert.ChangeType(input, typeof(T))समाधान प्रदान करती है। Convert.ChangeTypeविधि दूसरा तर्क के रूप में प्रदान प्रकार के लिए किसी भी वस्तु कन्वर्ट करने के लिए कोशिश करता है।

उदाहरण के लिए:

Type intType = typeof(Int32);
object value1 = 1000.1;

// Variable value2 is now an int with a value of 1000, the compiler 
// knows the exact type, it is safe to use and you will have autocomplete
int value2 = Convert.ChangeType(value1, intType);

// Variable value3 is now an int with a value of 1000, the compiler
// doesn't know the exact type so it will allow you to call any
// property or method on it, but will crash if it doesn't exist
dynamic value3 = Convert.ChangeType(value1, intType);

मैं, जेनरिक के साथ जवाब लिखा है क्योंकि मुझे लगता है कि यह एक बहुत ही संभावना कोड गंध की हस्ताक्षर जब आप कास्ट करना चाहते है a somethingकरने के लिए a something elseएक वास्तविक प्रकार से निपटने के बिना। उचित इंटरफेस के साथ, जो 99.9% बार आवश्यक नहीं होना चाहिए। शायद कुछ किनारे मामले हैं जब यह ध्यान में आता है कि यह समझ में आ सकता है, लेकिन मैं उन मामलों से बचने की सलाह दूंगा।

2 संपादित करें:

कुछ अतिरिक्त सुझाव:

  • अपने कोड को यथासंभव सुरक्षित रखने की कोशिश करें। यदि कंपाइलर प्रकार नहीं जानता है, तो यह जांच नहीं कर सकता है कि आपका कोड सही है या स्वत: पूर्ण जैसी चीजें काम नहीं करेंगी। सीधे शब्दों में कहा: यदि आप संकलन समय पर प्रकार (ओं) की भविष्यवाणी नहीं कर सकते हैं, तो कंपाइलर कैसे सक्षम होगा ?
  • यदि आप जो कक्षाएं एक सामान्य इंटरफ़ेस को लागू करने के साथ काम कर रहे हैं , तो आप उस इंटरफ़ेस पर मूल्य डाल सकते हैं। अन्यथा अपना स्वयं का इंटरफ़ेस बनाने पर विचार करें और कक्षाओं में उस इंटरफ़ेस को लागू करें।
  • यदि आप बाहरी पुस्तकालयों के साथ काम कर रहे हैं जिन्हें आप गतिशील रूप से आयात कर रहे हैं, तो एक सामान्य इंटरफ़ेस की भी जांच करें। अन्यथा इंटरफ़ेस को लागू करने वाले छोटे आवरण वर्ग बनाने पर विचार करें।
  • आप वस्तु पर कॉल करने के लिए है, लेकिन प्रकार के बारे में परवाह नहीं है, तो एक में मूल्य की दुकान चाहते हैं objectया dynamicचर।
  • जेनरिक पुन: प्रयोज्य कोड बनाने का एक शानदार तरीका हो सकता है जो बहुत से विभिन्न प्रकारों पर लागू होता है, इसमें शामिल सटीक प्रकारों को जानने के बिना।
  • यदि आप फंस गए हैं तो एक अलग दृष्टिकोण या कोड रिफ्लेक्टर पर विचार करें। क्या आपके कोड को वास्तव में गतिशील होना चाहिए? क्या इसमें किसी प्रकार का कोई हिसाब नहीं है?

145
मैं नहीं जानता कि यह कैसे ओपी की मदद कर रहा है। उसके पास एक प्रकार का चर है, ऐसा नहीं है T
नवाफाल

12
@nawfal, मूल रूप से लाइन Convert.ChangeType(input, typeof(T));समाधान देता है। आप आसानी typeof(T)से एक मौजूदा प्रकार के चर से बदल सकते हैं । एक बेहतर समाधान (यदि संभव हो) गतिशील प्रकार को एक साथ रोकने के लिए होगा।
Zyphrax

59
@Zyphrax, नहीं, इसके लिए अभी भी एक कास्ट की आवश्यकता है Tजो उपलब्ध नहीं है।
नवाफाल

4
मुझे पता है कि परिणामी वस्तु वास्तव में प्रकार की होती है Tलेकिन फिर भी आपको केवल objectएक संदर्भ के रूप में मिलता है । हम्म, मुझे इस सवाल को दिलचस्प लगा कि ओपी के पास केवल Typeचर और कोई अन्य जानकारी नहीं है। जैसे कि विधि हस्ताक्षर है Convert(object source, Type destination):) फिर भी मुझे उर बिंदु मिलता है
nawfal

10
यह इस सवाल का हल कैसे है? मुझे एक ही समस्या है और मेरे पास जेनेरिक <T> नहीं है। मेरे पास केवल एक प्रकार का चर है।
नूरी तसदीमिर

114

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

dynamic changedObj = Convert.ChangeType(obj, typeVar);
changedObj.Method();

ध्यान रखें कि "डायनेमिक" के उपयोग के साथ कंपाइलर स्थिर प्रकार की जाँच को दरकिनार कर रहा है जो कि सावधान न रहने पर संभावित रनटाइम त्रुटियों को प्रस्तुत कर सकता है।


19
यह सही जवाब है। डायनामिक कीवर्ड के बिना टाइपऑफ (बदला हुआ शब्द) "ऑब्जेक्ट" है। डायनामिक कीवर्ड के साथ यह त्रुटिपूर्ण रूप से काम करता है और टाइपऑफ (बदला हुआ) विशेष रूप से टाइप वीएआर के समान ही सही ढंग से प्रदर्शित होता है। इसके अतिरिक्त आपको (T) कास्ट की आवश्यकता नहीं है जो आप नहीं कर सकते हैं यदि आप टाइप नहीं जानते हैं।
२०:११ बजे जुगल

5
मुझे इस समाधान का उपयोग करते समय "ऑब्जेक्ट को IConvertible" अपवाद को लागू करना चाहिए। कोई मदद?
नूरी तसदीमिर

@NuriTasdemir हार्ड बताने के लिए, लेकिन मेरा मानना ​​है कि आप जो रूपांतरण कर रहे हैं, वह बिना IConvertible के संभव नहीं है। आपके रूपांतरण में कौन से प्रकार शामिल हैं?
मौलिक 13

जब यह काम करता है, तो गतिशीलता का उपयोग करने के साथ एक प्रदर्शन जुर्माना है। जब तक आप अन्य रनटाइम के साथ काम नहीं कर रहे हैं (जो कि गतिशीलता के लिए डिज़ाइन किए गए थे) मैं उनका उपयोग करने के खिलाफ सिफारिश करूंगा।
बोलो

19

यहाँ एक System.Typeगतिशील रूप से ऑब्जेक्ट को कास्ट करने का मेरा तरीका है, लेकिन जेनेरिक टाइप वेरिएबल के लिए नहीं, बल्कि :

मैं रन-टाइम का उपयोग करते हुए एक लैंबडा एक्सप्रेशन बनाता हूं, System.Linq.Expressionsप्रकार का Func<object, object>, जो इसके इनपुट को अनबॉक्स करता है, वांछित प्रकार का रूपांतरण करता है और फिर परिणाम को बॉक्सिंग देता है। एक नए को न केवल उन सभी प्रकारों की आवश्यकता होती है, जो उन्हें डाले जाते हैं, बल्कि उन प्रकारों के लिए भी होते हैं जो ढल जाते हैं (अनबॉक्सिंग स्टेप के कारण)। इन अभिव्यक्तियों को बनाने में अत्यधिक समय लगता है, क्योंकि प्रतिबिंब के कारण, संकलन और गतिशील विधि निर्माण जो हुड के तहत किया जाता है। सौभाग्य से एक बार निर्मित होने पर, अभिव्यक्तियों को बार-बार और उच्च ओवरहेड के बिना लागू किया जा सकता है, इसलिए मैं प्रत्येक को कैश करता हूं।

private static Func<object, object> MakeCastDelegate(Type from, Type to)
{
    var p = Expression.Parameter(typeof(object)); //do not inline
    return Expression.Lambda<Func<object, object>>(
        Expression.Convert(Expression.ConvertChecked(Expression.Convert(p, from), to), typeof(object)),
        p).Compile();
}

private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>> CastCache
= new Dictionary<Tuple<Type, Type>, Func<object, object>>();

public static Func<object, object> GetCastDelegate(Type from, Type to)
{
    lock (CastCache)
    {
        var key = new Tuple<Type, Type>(from, to);
        Func<object, object> cast_delegate;
        if (!CastCache.TryGetValue(key, out cast_delegate))
        {
            cast_delegate = MakeCastDelegate(from, to);
            CastCache.Add(key, cast_delegate);
        }
        return cast_delegate;
    }
}

public static object Cast(Type t, object o)
{
    return GetCastDelegate(o.GetType(), t).Invoke(o);
}

ध्यान दें कि यह जादू नहीं है। कास्टिंग कोड में नहीं होती है, जैसा कि यह dynamicकीवर्ड के साथ करता है , केवल ऑब्जेक्ट का अंतर्निहित डेटा परिवर्तित हो जाता है। संकलन-समय पर हम अभी भी श्रमसाध्य रूप से यह जानने के लिए शेष हैं कि हमारी वस्तु किस प्रकार की हो सकती है, जिससे यह समाधान अव्यावहारिक हो जाता है। मैंने इसे मनमाने प्रकारों द्वारा परिभाषित रूपांतरण ऑपरेटरों को आह्वान करने के लिए एक हैक के रूप में लिखा था, लेकिन शायद कोई व्यक्ति बेहतर उपयोग के मामले को खोज सकता है।


2
आवश्यकता हैusing System.Linq.Expressions;
एरोन डी

4
मेरे लिए यह Zyphrax के उत्तर जैसी ही समस्या से ग्रस्त है। मैं लौटी हुई वस्तु पर तरीके नहीं ला सकता क्योंकि यह अभी भी "वस्तु" प्रकार की है। चाहे मैं उसकी विधि ("ए" नीचे) या आपकी विधि ("बी" नीचे) का उपयोग करता हूं, मुझे (t) कास्ट पर एक ही त्रुटि मिलती है - "'t' एक चर है, लेकिन इसका उपयोग एक प्रकार की तरह किया जाता है।Type t = typeof(MyGeneric<>).MakeGenericType(obj.OutputType); var a = (t)Convert.ChangeType(obj, t); var b = (t)Caster.Cast(t, obj);
mususbolla

@mususbolla Zyphrax का मूल उत्तर जेनेरिक और टाइप चर का उपयोग करता है, नहीं Type। यदि आपके पास सभी प्रकार की वस्तु है तो आप सामान्य कास्टिंग सिंटैक्स का उपयोग नहीं कर सकते। यदि आप ऑब्जेक्ट को संकलित समय पर कुछ टाइप टी के रूप में उपयोग करने में सक्षम होना चाहते हैं, तो रनटाइम नहीं, तो आपको इसे एक प्रकार के चर या सिर्फ वास्तविक प्रकार के नाम का उपयोग करके डालना होगा। आप Zaphrax के उत्तर का उपयोग करके पूर्व कर सकते हैं।
एशले

8

सादगी के लिए मुक्केबाजी और अलग-अलग बॉक्सिंग करना, वंशानुक्रम पदानुक्रम के साथ कास्टिंग में शामिल कोई विशिष्ट रनटाइम कार्रवाई नहीं है। यह ज्यादातर एक संकलन समय बात है। अनिवार्य रूप से, एक कलाकार कंपाइलर को चर के मूल्य को दूसरे प्रकार के रूप में मानने के लिए कहता है।

आप कलाकारों के बाद क्या कर सकते थे? आप प्रकार नहीं जानते हैं, इसलिए आप इस पर कोई भी तरीका नहीं बता पाएंगे। कोई विशेष बात नहीं है जो आप कर सकते हैं। विशेष रूप से, यह केवल तभी उपयोगी हो सकता है जब आप संकलित समय पर संभावित प्रकारों को जानते हों, इसे मैन्युअल रूप से डालें और प्रत्येक मामले को ifबयानों के साथ अलग से संभालें :

if (type == typeof(int)) {
    int x = (int)obj;
    DoSomethingWithInt(x);
} else if (type == typeof(string)) {
    string s = (string)obj;
    DoSomethingWithString(s);
} // ...

1
क्या आप कृपया मेरे प्रश्न के संबंध में स्पष्ट कर सकते हैं?
तेरिंगोस्ट्रारस

जो मैं समझाने की कोशिश कर रहा हूं, उसके बाद आप क्या कर पाएंगे? आप ज्यादा कुछ नहीं कर सकते हैं क्योंकि C # संकलक को वस्तु के साथ उपयोगी काम करने में सक्षम होने के लिए स्थैतिक टाइपिंग की आवश्यकता होती है
मेहरदाद अफश्री

आप सही हे। मैं दो प्रकार के अपेक्षित प्रकार जानता हूं जो 'ऑब्जेक्ट' के रूप में विधि को भेजे जाते हैं। मैं चर में संग्रहीत अपेक्षित प्रकारों को डालना चाहता हूं, और उन्हें संग्रह में जोड़ना चाहता हूं। टाइप पर शाखा करना और सामान्य कलाकारों का प्रयास करना और त्रुटियों को पकड़ना बहुत आसान है।
तेरिंगोस्तारस

4
आपका उत्तर अच्छा है, लेकिन सिर्फ नाइट-पिकी होने के लिए, मैं ध्यान देता हूं कि जातियां कभी भी चर को प्रभावित नहीं करती हैं । यह एक कास्ट करने के लिए कानूनी नहीं है चर एक अन्य प्रकार के एक चर करने के लिए; चर प्रकार C # में अपरिवर्तनीय हैं। आप केवल चर में संग्रहीत मान को किसी अन्य प्रकार में डाल सकते हैं ।
एरिक लिपर्ट

क्या गतिशील टाइपिंग का C # 4.0 परिचय इस उत्तर को बदल देता है?
डेनियल टी।

6

तुम ऐसा कैसे कर सकते हो? आपको टाइप टी के एक चर या क्षेत्र की आवश्यकता है जहां आप कलाकारों के बाद ऑब्जेक्ट को स्टोर कर सकते हैं, लेकिन अगर आप केवल रनटाइम पर ही टी जानते हैं तो आपके पास ऐसा चर या क्षेत्र कैसे हो सकता है? तो, नहीं, यह संभव नहीं है।

Type type = GetSomeType();
Object @object = GetSomeObject();

??? xyz = @object.CastTo(type); // How would you declare the variable?

xyz.??? // What methods, properties, or fields are valid here?

3
यदि आप एक सामान्य वर्ग का उपयोग कर रहे हैं, जो टाइप टी के वापसी मूल्य के साथ एक विधि को परिभाषित करता है, तो आपको ऐसा करने की आवश्यकता हो सकती है। जैसे टी के उदाहरण के लिए एक स्ट्रिंग को पार्स करना और उसे वापस करना।
ओलिवर फ्रेडरिक

7
यह सौभाग्य से सही उत्तर नहीं है। देखिए मौलवी 13 का जवाब
२०:१५ बजे

3
स्वर्ग के नाम पर आप कहाँ CastToपर एक विधि पाते हैं Object?
प्रोफे डे

3

जब एनम प्रकार के लिए कास्टिंग की बात आती है:

private static Enum GetEnum(Type type, int value)
    {
        if (type.IsEnum)
            if (Enum.IsDefined(type, value))
            {
                return (Enum)Enum.ToObject(type, value);
            }

        return null;
    }

और आप इसे इस तरह कहेंगे:

var enumValue = GetEnum(typeof(YourEnum), foo);

यह मेरे लिए आवश्यक था कि इंटम वैल्यू द्वारा कई एनुम प्रकारों का विवरण विशेषता मूल्य प्राप्त करने के लिए:

public enum YourEnum
{
    [Description("Desc1")]
    Val1,
    [Description("Desc2")]
    Val2,
    Val3,
}

public static string GetDescriptionFromEnum(Enum value, bool inherit)
    {
        Type type = value.GetType();

        System.Reflection.MemberInfo[] memInfo = type.GetMember(value.ToString());

        if (memInfo.Length > 0)
        {
            object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), inherit);
            if (attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }

        return value.ToString();
    }

और फिर:

string description = GetDescriptionFromEnum(GetEnum(typeof(YourEnum), foo));
string description2 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum2), foo2));
string description3 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum3), foo3));

वैकल्पिक रूप से (बेहतर दृष्टिकोण), इस तरह की कास्टिंग इस तरह दिख सकती है:

 private static T GetEnum<T>(int v) where T : struct, IConvertible
    {
        if (typeof(T).IsEnum)
            if (Enum.IsDefined(typeof(T), v))
            {
                return (T)Enum.ToObject(typeof(T), v);
            }

        throw new ArgumentException(string.Format("{0} is not a valid value of {1}", v, typeof(T).Name));
    }

1

Zyphrax के उत्तर (इंटरफ़ेस को लागू करने के लिए छोड़कर) का उपयोग करते समय "ऑब्जेक्ट को IConvertible" अपवाद को लागू करने के लिए कुछ भी नहीं खोजने के बाद .. मैंने कुछ अपरंपरागत की कोशिश की और अपनी स्थिति के लिए काम किया।

Newtonsoft.Json नगेट पैकेज का उपयोग ...

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);

1

नुकसान, समस्या यह है कि आपके पास एक टी नहीं है।

आपके पास केवल एक प्रकार का चर है।

एमएस के संकेत, अगर आप कुछ ऐसा कर सकते हैं

TryCast<typeof(MyClass)>

अगर हमारी सभी समस्याओं का समाधान होगा।


0

मैं कभी नहीं समझ पाऊंगा कि आपको टिप्पणी छोड़ने के लिए 50 प्रतिष्ठा तक की आवश्यकता क्यों है, लेकिन मुझे सिर्फ इतना कहना था कि @ जवाब सही है कि मैं क्या देख रहा था और उम्मीद है कि कोई और होगा।

मेरे उदाहरण में, मेरे पास ActionFilterAttribute है जो मैं एक json पैच दस्तावेज़ के मूल्यों को अद्यतन करने के लिए उपयोग कर रहा था। मैंने टी मॉडल को पैच डॉक्यूमेंट के लिए क्या नहीं किया था, मुझे उसे सीज करना था और उसे एक सादे JsonPatchDocument में डीराइज़ करना था, उसे संशोधित किया, फिर क्योंकि मेरे पास टाइप था, सीरियलाइज़ करें और इसे फिर से टाइप करने के लिए डिसेरलाइज़ करें।

Type originalType = //someType that gets passed in to my constructor.

var objectAsString = JsonConvert.SerializeObject(myObjectWithAGenericType);
var plainPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument>(objectAsString);

var plainPatchDocumentAsString= JsonConvert.SerializeObject(plainPatchDocument);
var modifiedObjectWithGenericType = JsonConvert.DeserializeObject(plainPatchDocumentAsString, originalType );

-1
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}

2
क्या आप यह बता सकते हैं कि यह उत्तर अन्य उत्तरों से कैसे भिन्न है और यह समाधान कहां तक ​​उचित है?
क्लॉस गुटर्टर


-2

यदि आपको गंतव्य प्रकार को जाने बिना रनटाइम पर ऑब्जेक्ट्स डालने की आवश्यकता है, तो आप एक गतिशील कनवर्टर बनाने के लिए प्रतिबिंब का उपयोग कर सकते हैं।

यह एक सरलीकृत संस्करण है (कैशिंग जनरेटेड विधि के बिना):

    public static class Tool
    {
            public static object CastTo<T>(object value) where T : class
            {
                return value as T;
            }

            private static readonly MethodInfo CastToInfo = typeof (Tool).GetMethod("CastTo");

            public static object DynamicCast(object source, Type targetType)
            {
                return CastToInfo.MakeGenericMethod(new[] { targetType }).Invoke(null, new[] { source });
            }
    }

तो आप इसे कॉल कर सकते हैं:

    var r = Tool.DynamicCast(myinstance, typeof (MyClass));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.