Convert.ChangeType () अशक्त प्रकारों पर विफल रहता है


301

मैं एक स्ट्रिंग को ऑब्जेक्ट प्रॉपर्टी वैल्यू में बदलना चाहता हूं, जिसका नाम मेरे पास एक स्ट्रिंग है। मैं ऐसा करने की कोशिश कर रहा हूं:

string modelProperty = "Some Property Name";
string value = "SomeValue";
var property = entity.GetType().GetProperty(modelProperty);
if (property != null) {
    property.SetValue(entity, 
        Convert.ChangeType(value, property.PropertyType), null);
}

यह समस्या विफल हो रही है और जब संपत्ति प्रकार एक अशक्त प्रकार है तो एक अमान्य कास्ट एक्सेप्शन फेंक रहा है। यह उन मूल्यों का मामला नहीं है जो रूपांतरित होने में असमर्थ हैं - वे काम करेंगे यदि मैं इसे मैन्युअल रूप से करता हूं (जैसे DateTime? d = Convert.ToDateTime(value);) मैंने कुछ अज्ञात प्रश्न देखे हैं, लेकिन फिर भी यह काम करने के लिए नहीं मिल सकता है।


1
मैं पेटाकोको 4.0.3 के साथ ExecuteScalar <int?> का उपयोग कर रहा हूं और यह उसी कारण से विफल हो रहा है: रिटर्न (T) Convert.ChangeType (वैल, टाइपोफ (T)) लाइन 554 पर
लैरी

जवाबों:


409

अनछुए, लेकिन शायद कुछ इस तरह से काम करेंगे:

string modelProperty = "Some Property Name";
string value = "Some Value";

var property = entity.GetType().GetProperty(modelProperty);
if (property != null)
{
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(entity, safeValue, null);
}

12
बस कोड के उस टुकड़े की जरूरत है। Nullable.GetUnderlyingType के लिए धन्यवाद! जब मैंने एक गरीब आदमी की ModelBinder को एक प्रोजेक्ट के लिए बनाया, तो मुझे बहुत मदद मिली। मुझ पर आपकी एक बीयर उधार है!
मैक्सिम रूइलर

3
शायद (value == null) ? nullउपयोग के बजाय (value == null) ? default(t)?
थ्रेडस्टर

स्ट्रिंग के लिए अद्वितीय पहचानकर्ता के लिए काम नहीं करता है।
एंडर्स लिंडेन

क्या safeValueचर बनाने के लिए किसी विशेष कारण के रूप में सिर्फ आश्वस्त करने का विरोध किया गया है value?
coloradocolby

@threadster आप 'टाइप' के डिफॉल्ट ऑपरेटर ओना वेरिएबल का उपयोग नहीं कर सकते। देखें stackoverflow.com/questions/325426/...
andy250

75

आपको ऐसा करने के लिए अंतर्निहित प्रकार प्राप्त करना होगा ...

यह कोशिश करो, मैंने इसे सफलतापूर्वक जेनरिक के साथ प्रयोग किया है:

//Coalesce to get actual property type...
Type t = property.PropertyType();
t = Nullable.GetUnderlyingType(t) ?? t;

//Coalesce to set the safe value using default(t) or the safe type.
safeValue = value == null ? default(t) : Convert.ChangeType(value, t);

मैं अपने कोड में कई स्थानों पर इसका उपयोग करता हूं, एक उदाहरण एक सहायक विधि है जिसका उपयोग मैं डेटाबेस मानों को एक कैफे में परिवर्तित करने के लिए करता हूं:

public static T GetValue<T>(this IDataReader dr, string fieldName)
{
    object value = dr[fieldName];

    Type t = typeof(T);
    t = Nullable.GetUnderlyingType(t) ?? t;

    return (value == null || DBNull.Value.Equals(value)) ? 
        default(T) : (T)Convert.ChangeType(value, t);
}

का उपयोग कर बुलाया:

string field1 = dr.GetValue<string>("field1");
int? field2 = dr.GetValue<int?>("field2");
DateTime field3 = dr.GetValue<DateTime>("field3");

मैंने http://www.endwithsaurus.com/2010_07_01_archive.html पर इस सहित ब्लॉग पोस्टों की एक श्रृंखला लिखी है (नीचे परिशिष्ट में स्क्रॉल करें, @ जॉनमोनेसटायर वास्तव में मेरे मूल कोड में बग को देखा था, जिसने मुझे उसी मार्ग से नीचे ले जाया था जो आप हैं। अब चालू)। मेरे पास उस पोस्ट के बाद से कुछ छोटे संशोधन हैं, जिसमें एनम प्रकारों का रूपांतरण भी शामिल है, इसलिए यदि आपकी संपत्ति एक एनम है तो आप अभी भी उसी विधि कॉल का उपयोग कर सकते हैं। बस एनम प्रकारों के लिए जाँच करने के लिए एक पंक्ति जोड़ें और आप कुछ का उपयोग करके दौड़ से दूर हैं:

if (t.IsEnum)
    return (T)Enum.Parse(t, value);

आम तौर पर आपको Parse के बजाय TryParse की जाँच करने या उपयोग करने में कुछ त्रुटि होगी, लेकिन आपको चित्र मिल जाएगा।


धन्यवाद - मुझे अभी भी एक कदम याद आ रहा है या कुछ समझ नहीं आ रहा है। मैं एक प्रॉपर्टी वैल्यू सेट करने की कोशिश कर रहा हूं, मुझे वह वस्तु क्यों मिल रही है, जो अंडर-टाइपिंग में है? मुझे यह भी निश्चित नहीं है कि मुझे अपने कोड से आपकी जैसी एक्सटेंशन विधि कैसे प्राप्त करनी चाहिए। मुझे नहीं पता होगा कि मूल्य की तरह कुछ करने के लिए प्रकार क्या होगा। हेल्पर <Int32?> ()।
आईबैनो

@iboeno - क्षमा करें, एक बैठक में बंद था इसलिए मैं आपको डॉट्स कनेक्ट करने में मदद नहीं कर सका। खुशी है कि आपको एक समाधान मिला।
BenAlabaster

9

यह एक उदाहरण के लिए थोड़ा लंबा-ईश है, लेकिन यह अपेक्षाकृत मजबूत दृष्टिकोण है, और अज्ञात मूल्य से अज्ञात प्रकार के लिए कास्टिंग के कार्य को अलग करता है

मेरे पास एक ट्राइकास्ट विधि है जो कुछ इसी तरह का काम करती है, और अशक्त प्रकारों को ध्यान में रखती है।

public static bool TryCast<T>(this object value, out T result)
{
    var type = typeof (T);

    // If the type is nullable and the result should be null, set a null value.
    if (type.IsNullable() && (value == null || value == DBNull.Value))
    {
        result = default(T);
        return true;
    }

    // Convert.ChangeType fails on Nullable<T> types.  We want to try to cast to the underlying type anyway.
    var underlyingType = Nullable.GetUnderlyingType(type) ?? type;

    try
    {
        // Just one edge case you might want to handle.
        if (underlyingType == typeof(Guid))
        {
            if (value is string)
            {
                value = new Guid(value as string);
            }
            if (value is byte[])
            {
                value = new Guid(value as byte[]);
            }

            result = (T)Convert.ChangeType(value, underlyingType);
            return true;
        }

        result = (T)Convert.ChangeType(value, underlyingType);
        return true;
    }
    catch (Exception ex)
    {
        result = default(T);
        return false;
    }
}

बेशक TryCast एक प्रकार का पैरामीटर के साथ एक विधि है, इसलिए इसे गतिशील रूप से कॉल करने के लिए आपको MethodInfo का निर्माण स्वयं करना होगा:

var constructedMethod = typeof (ObjectExtensions)
    .GetMethod("TryCast")
    .MakeGenericMethod(property.PropertyType);

फिर वास्तविक संपत्ति मूल्य निर्धारित करने के लिए:

public static void SetCastedValue<T>(this PropertyInfo property, T instance, object value)
{
    if (property.DeclaringType != typeof(T))
    {
        throw new ArgumentException("property's declaring type must be equal to typeof(T).");
    }

    var constructedMethod = typeof (ObjectExtensions)
        .GetMethod("TryCast")
        .MakeGenericMethod(property.PropertyType);

    object valueToSet = null;
    var parameters = new[] {value, null};
    var tryCastSucceeded = Convert.ToBoolean(constructedMethod.Invoke(null, parameters));
    if (tryCastSucceeded)
    {
        valueToSet = parameters[1];
    }

    if (!property.CanAssignValue(valueToSet))
    {
        return;
    }
    property.SetValue(instance, valueToSet, null);
}

और property.CanAssignValue से निपटने के लिए विस्तार विधियाँ ...

public static bool CanAssignValue(this PropertyInfo p, object value)
{
    return value == null ? p.IsNullable() : p.PropertyType.IsInstanceOfType(value);
}

public static bool IsNullable(this PropertyInfo p)
{
    return p.PropertyType.IsNullable();
}

public static bool IsNullable(this Type t)
{
    return !t.IsValueType || Nullable.GetUnderlyingType(t) != null;
}

6

मुझे इसी तरह की आवश्यकता थी, और ल्यूक के जवाब ने मुझे दिशा में बताया। मैं इस सामान्य कार्य को आसान बनाने के लिए आया था।

    public static Tout CopyValue<Tin, Tout>(Tin from, Tout toPrototype)
    {
        Type underlyingT = Nullable.GetUnderlyingType(typeof(Tout));
        if (underlyingT == null)
        { return (Tout)Convert.ChangeType(from, typeof(Tout)); }
        else
        { return (Tout)Convert.ChangeType(from, underlyingT); }
    }

उपयोग इस प्रकार है:

        NotNullableDateProperty = CopyValue(NullableDateProperty, NotNullableDateProperty);

ध्यान दें कि दूसरे पैरामीटर को केवल फ़ंक्शन को दिखाने के लिए एक प्रोटोटाइप के रूप में उपयोग किया जाता है कि रिटर्न वैल्यू कैसे डाली जाए, इसलिए यह वास्तव में गंतव्य संपत्ति नहीं है। मतलब आप भी कुछ ऐसा कर सकते हैं:

        DateTime? source = new DateTime(2015, 1, 1);
        var dest = CopyValue(source, (string)null);

मैंने इसका उपयोग इस तरह से किया क्योंकि आप गुणों के साथ उपयोग नहीं कर सकते। जैसा है, यह गुण और चर के साथ काम कर सकता है। यदि आप चाहते थे, तो इसके बजाय आप टाइप करने के लिए एक अधिभार भी बना सकते हैं।


0

धन्यवाद @LukeH
मैं थोड़ा बदल गया:

public static object convertToPropType(PropertyInfo property, object value)
{
    object cstVal = null;
    if (property != null)
    {
        Type propType = Nullable.GetUnderlyingType(property.PropertyType);
        bool isNullable = (propType != null);
        if (!isNullable) { propType = property.PropertyType; }
        bool canAttrib = (value != null || isNullable);
        if (!canAttrib) { throw new Exception("Cant attrib null on non nullable. "); }
        cstVal = (value == null || Convert.IsDBNull(value)) ? null : Convert.ChangeType(value, propType);
    }
    return cstVal;
}

0

मैंने इसे इस तरह से किया

public static List<T> Convert<T>(this ExcelWorksheet worksheet) where T : new()
    {
        var result = new List<T>();
        int colCount = worksheet.Dimension.End.Column;  //get Column Count
        int rowCount = worksheet.Dimension.End.Row;

        for (int row = 2; row <= rowCount; row++)
        {
            var obj = new T();
            for (int col = 1; col <= colCount; col++)
            {

                var value = worksheet.Cells[row, col].Value?.ToString();
                PropertyInfo propertyInfo = obj.GetType().GetProperty(worksheet.Cells[1, col].Text);
                propertyInfo.SetValue(obj, Convert.ChangeType(value, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType), null);

            }
            result.Add(obj);
        }

        return result;
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.