प्रतिबिंब के साथ 'कास्टिंग'


81

निम्नलिखित नमूना कोड पर विचार करें:

class SampleClass
{
    public long SomeProperty { get; set; }
}

public void SetValue(SampleClass instance, decimal value)
{
    // value is of type decimal, but is in reality a natural number => cast
    instance.SomeProperty = (long)value;
}

अब मुझे परावर्तन के माध्यम से कुछ ऐसा ही करने की आवश्यकता है:

void SetValue(PropertyInfo info, object instance, object value)
{
    // throws System.ArgumentException: Decimal can not be converted to Int64
    info.SetValue(instance, value)  
}

ध्यान दें कि मैं यह नहीं मान सकता कि प्रॉपर्टीइंफो हमेशा एक लंबा प्रतिनिधित्व करता है, न ही वह मूल्य हमेशा एक दशमलव होता है। हालांकि, मुझे पता है कि उस संपत्ति के लिए सही प्रकार से मूल्य डाला जा सकता है।

मैं प्रतिबिंब के माध्यम से PropertyInfo उदाहरण द्वारा दर्शाए गए प्रकार के लिए 'मान' पैरामीटर कैसे बदल सकता हूं?

जवाबों:


134
void SetValue(PropertyInfo info, object instance, object value)
{
    info.SetValue(instance, Convert.ChangeType(value, info.PropertyType));
}

1
ध्यान दें कि Convert.ChangeType(value, property.PropertyType);अभी भी विफल हो सकता है अगर इंटरफ़ेस को valueलागू नहीं करता IConvertibleहै। उदाहरण के लिए, यदि info.PropertyTypeकुछ हैIEnumerable
derekantrican 16

42

थॉमस जवाब केवल उन प्रकारों के लिए काम करता है जो IConvertible इंटरफ़ेस को लागू करते हैं:

रूपांतरण को सफल होने के लिए, मान को IConvertible इंटरफ़ेस को लागू करना चाहिए, क्योंकि विधि केवल कॉल को एक उपयुक्त IConvertible विधि से लपेटता है। विधि की आवश्यकता है कि रूपांतरण के लिए मूल्य रूपांतरण का समर्थन किया जाए।

यह कोड एक लाइनक अभिव्यक्ति को संकलित करता है जो अनबॉक्सिंग (यदि आवश्यक हो) और रूपांतरण:

    public static object Cast(this Type Type, object data)
    {
        var DataParam = Expression.Parameter(typeof(object), "data");
        var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type));

        var Run = Expression.Lambda(Body, DataParam).Compile();
        var ret = Run.DynamicInvoke(data);
        return ret;
    }

जिसके परिणामस्वरूप लैम्ब्डा अभिव्यक्ति (TOut) (TIn) डेटा के बराबर है जहां TIn मूल डेटा का प्रकार है और TOut दिया गया प्रकार है


2
यह वास्तव में वह उत्तर है जिसकी मुझे तलाश थी। गैर-IConvertible गतिशील कास्टिंग।
jnm2

1
हेह मैं- अगर मैं ओपी होता।
jnm2

1
उम्मीद थी कि यह मुझे बचाने के लिए जब IEnumerable<object>(जहां उन वस्तुओं तार कर रहे हैं) कास्ट करने की कोशिश कर रहा होगा IEnumerable<string>। दुर्भाग्य से मुझे त्रुटियाँ मिल रही हैंUnable to cast object of type 'System.Collections.Generic.IEnumerable'1[System.Object]' to type 'System.Collections.Generic.IEnumerable'1[System.String]'.
derekantrican

41

थॉमस का उत्तर सही है, लेकिन मुझे लगा कि मैं अपनी खोज को जोड़ दूंगा कि Convert.ChangeType रूपांतरण को अशक्त प्रकारों से नहीं संभालता है। अशक्त प्रकारों को संभालने के लिए, मैंने निम्नलिखित कोड का उपयोग किया:

void SetValue(PropertyInfo info, object instance, object value)
{
    var targetType = info.PropertyType.IsNullableType() 
         ? Nullable.GetUnderlyingType(info.PropertyType) 
         : info.PropertyType; 
    var convertedValue = Convert.ChangeType(value, targetType);

    info.SetValue(instance, convertedValue, null);
}

यह कोड निम्नलिखित एक्सटेंशन विधि का उपयोग करता है:

public static class TypeExtensions
{
  public static bool IsNullableType(this Type type)
  {
    return type.IsGenericType 
    && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
  }

10

Jeroenh के उत्तर में योगदान करते हुए, मैं उस Convert.ChangeType को एक शून्य मान के साथ क्रैश करता हूं, इसलिए परिवर्तित मान प्राप्त करने के लिए लाइन होनी चाहिए:

var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);

2

जब टाइप एक अशक्त गाइड है तो उपरोक्त प्रस्तावित समाधानों में से कोई भी काम नहीं करता है। ' System.DBNull' से ' System.Guid' अपवाद के लिए अमान्य कास्ट फेंक दिया जाता हैConvert.ChangeType

उस परिवर्तन को ठीक करने के लिए:

var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);

2
यह समस्या दिशानिर्देश के लिए विशेष नहीं है, बल्कि इस तथ्य के कारण है कि जब आप डेटाबेस से ADO .Net के माध्यम से शून्य मान प्राप्त कर रहे हैं, तो DBNull.Valueइसके बजाय null। आप उदाहरण के लिए, अशक्त int के साथ देखेंगे।
फिरोज

0

यह एक बहुत पुराना सवाल है, लेकिन मुझे लगा कि मैं ASP.NET कोर गोगलर्स के लिए चाइम करूंगा।

ASP.NET कोर में, .IsNullableType()(अन्य परिवर्तनों के बीच) संरक्षित है , इसलिए कोड एक अलग है। यहाँ पर @ jeroenh का उत्तर ASP.NET कोर में काम करने के लिए संशोधित किया गया है:

void SetValue(PropertyInfo info, object instance, object value)
{
    Type proptype = info.PropertyType;
    if (proptype.IsGenericType && proptype.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
    {
        proptype = new NullableConverter(info.PropertyType).UnderlyingType;
    }

    var convertedValue = Convert.ChangeType(value, proptype);
    info.SetValue(instance, convertedValue);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.