एक सामान्य पैरामीटर के रूप में अशक्त प्रकार संभव है?


288

मैं कुछ इस तरह करना चाहता हूं:

myYear = record.GetValueOrNull<int?>("myYear"),

सामान्य पैरामीटर के रूप में अशक्त प्रकार पर ध्यान दें।

चूंकि GetValueOrNullसमारोह शून्य हो सकता है इसलिए मेरा पहला प्रयास यह था:

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : class
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
    {
        return (T)columnValue;
    }
    return null;
}

लेकिन अब मुझे जो त्रुटि मिल रही है, वह है:

प्रकार 'int?' सामान्य प्रकार या विधि में पैरामीटर 'टी' के रूप में इसका उपयोग करने के लिए एक संदर्भ प्रकार होना चाहिए

सही! Nullable<int>एक है struct! इसलिए मैंने कक्षा की बाधा को एक structबाधा में बदलने की कोशिश की (और एक साइड इफेक्ट के रूप में और nullअधिक वापस नहीं किया जा सकता है ):

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : struct

अब असाइनमेंट:

myYear = record.GetValueOrNull<int?>("myYear");

निम्नलिखित त्रुटि देता है:

प्रकार 'int?' सामान्य प्रकार या विधि में पैरामीटर 'टी' के रूप में इसका उपयोग करने के लिए एक गैर-अशक्त मूल्य प्रकार होना चाहिए

क्या एक अशक्त प्रकार को एक सामान्य पैरामीटर के रूप में निर्दिष्ट करना संभव है?


3
Pls कृपया अपने हस्ताक्षर बनाने IDataRecordसे DbDataRecord..
nawfal

जवाबों:


262

वापसी प्रकार को Nullable में बदलें, और गैर अशक्त पैरामीटर के साथ विधि को कॉल करें

static void Main(string[] args)
{
    int? i = GetValueOrNull<int>(null, string.Empty);
}


public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
        return (T)columnValue;

    return null;
}

1
मेरा सुझाव है कि आप "columnValue == DBNull.Value" का उपयोग 'ऑपरेटर' के बजाय करते हैं, क्योंकि इसका थोड़ा तेज़ =)
driAn

40
व्यक्तिगत प्राथमिकता, लेकिन आप संक्षिप्त रूप T का उपयोग कर सकते हैं? Nullable <T> के बजाय
Dunc

11
यह मूल्य प्रकारों के लिए ठीक है, लेकिन फिर मुझे लगता है कि यह संदर्भ प्रकारों (जैसे GetValueOrNull <string>) के साथ बिल्कुल भी काम नहीं करेगा क्योंकि C # Nullable <(रेफ टाइप)> "string" की तरह नहीं लगता है। नीचे रॉबर्ट सी बार्थ और जेम्स जोन्स के समाधान, मुझे बहुत बेहतर लगते हैं यदि आपकी आवश्यकता है।
बेकर

2
@ बकर - सही, इसलिए "जहां टी: संरचना", यदि आप संदर्भ प्रकार चाहते हैं तो आप "जहां टी: क्लास" के साथ एक समान विधि बना सकते हैं
ग्रेग डीन

4
@Greg - यकीन है, लेकिन तब आपको दूसरी विधि की आवश्यकता होती है, और आप नाम को अधिभार नहीं दे सकते। जैसा कि मैं कहता हूं, यदि आप वैल और रेफ दोनों प्रकारों को संभालना चाहते हैं, तो मुझे लगता है कि इस पृष्ठ पर क्लीनर समाधान प्रस्तुत किए गए हैं।
बेकर

107
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
    object val = rdr[index];

    if (!(val is DBNull))
        return (T)val;

    return default(T);
}

बस इसे इस तरह उपयोग करें:

decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
string Unit = rdr.GetValueOrDefault<string>(2);

6
इसे छोटा किया जा सकता है: रिटर्न rdr.IsDBNull (सूचकांक)? डिफ़ॉल्ट (टी): (टी) आरडीआर [सूचकांक];
फेन

11
मुझे लगता है कि यह प्रश्न स्पष्ट रूप से अशक्त होना चाहता है , न कि डिफ़ॉल्ट (टी)
mafu

5
@mafu डिफ़ॉल्ट (T) संदर्भ प्रकारों के लिए शून्य, और संख्यात्मक प्रकार के लिए 0, समाधान को और अधिक लचीला बना देगा।
जेम्स जोन्स

2
मुझे लगता है कि यह स्पष्ट है या तो इस कॉल करने के लिए GetValueOrDefaultस्पष्ट करने के लिए है कि यह रिटर्न default(T)के बजाय null। वैकल्पिक रूप से, आप इसे एक अपवाद फेंक सकते हैं यदि Tयह अशक्त नहीं है।
सैम

इस पद्धति के बहुत सारे फायदे हैं, और आपको अशक्त के अलावा कुछ और लौटाने के बारे में सोचने के लिए मजबूर करता है।
शेन

61

बस अपने मूल कोड के लिए दो बातें - हटाने whereबाधा है, और अंतिम बदलने returnसे return nullकरने के लिए return default(T)। इस तरह आप जो चाहें टाइप कर सकते हैं।

वैसे, आप isअपने ifबयान को बदलकर इसके उपयोग से बच सकते हैं if (columnValue != DBNull.Value)


4
यह समाधान काम नहीं करता है, क्योंकि
ग्रेग डीन

15
यह काम करता है यदि वह जिस प्रकार से गुजरता है वह int है? यह NULL को वापस लौटा देगा, जैसे वह चाहता है। यदि वह इंट को टाइप के रूप में पास करता है, तो यह 0 से वापस आ जाएगा क्योंकि एक इंट NULL नहीं हो सकता है। इस तथ्य के अलावा कि मैंने इसकी कोशिश की और यह पूरी तरह से काम करता है।
रॉबर्ट सी। बार्थ

2
यह सबसे सही और लचीला जवाब है। हालांकि, return defaultपर्याप्त है (आपको इसकी आवश्यकता नहीं है (T), संकलक इसे हस्ताक्षर रिटर्न प्रकार से अनुमान लगाएगा)।
McGuireV10

5

अस्वीकरण: यह उत्तर काम करता है, लेकिन केवल शैक्षिक उद्देश्यों के लिए है। :) जेम्स जोन्स का समाधान शायद यहां सबसे अच्छा है और निश्चित रूप से मैं जिसके साथ जाऊंगा।

dynamicकम सुरक्षित होने पर C # 4.0 का कीवर्ड इसे और भी आसान बनाता है:

public static dynamic GetNullableValue(this IDataRecord record, string columnName)
{
  var val = reader[columnName];

  return (val == DBNull.Value ? null : val);
}

अब आपको RHS पर स्पष्ट प्रकार के संकेत की आवश्यकता नहीं है:

int? value = myDataReader.GetNullableValue("MyColumnName");

वास्तव में, आपको इसकी आवश्यकता भी नहीं है!

var value = myDataReader.GetNullableValue("MyColumnName");

value अब एक इंट, या एक स्ट्रिंग, या जो भी प्रकार DB से लौटाया जाएगा।

एकमात्र समस्या यह है कि यह आपको एलएचएस पर गैर-अशक्त प्रकारों का उपयोग करने से नहीं रोकता है, इस स्थिति में आपको इसके बजाय एक बुरा रनटाइम अपवाद मिलेगा:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type

जैसा कि उपयोग करने वाले सभी कोड के साथ dynamic: कैविटी कोडर।


4

बस इसके समान कुछ अविश्वसनीय करना था। मेरा कोड:

public T IsNull<T>(this object value, T nullAlterative)
{
    if(value != DBNull.Value)
    {
        Type type = typeof(T);
        if (type.IsGenericType && 
            type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
        {
            type = Nullable.GetUnderlyingType(type);
        }

        return (T)(type.IsEnum ? Enum.ToObject(type, Convert.ToInt32(value)) :
            Convert.ChangeType(value, type));
    }
    else 
        return nullAlternative;
}

3

मुझे लगता है कि आप संदर्भ प्रकार और संरचना प्रकार को संभालना चाहते हैं। मैं इसका उपयोग XML तत्व स्ट्रिंग्स को अधिक टाइप किए गए प्रकार में परिवर्तित करने के लिए करता हूं। आप प्रतिबिंब के साथ nullAlternative को हटा सकते हैं। फॉर्मरप्रोवाइडर संस्कृति पर निर्भर को संभालने के लिए है। ' या ',' विभाजक जैसे डेसीमल या इन्टल्स और डबल्स। यह काम कर सकता है:

public T GetValueOrNull<T>(string strElementNameToSearchFor, IFormatProvider provider = null ) 
    {
        IFormatProvider theProvider = provider == null ? Provider : provider;
        XElement elm = GetUniqueXElement(strElementNameToSearchFor);

        if (elm == null)
        {
            object o =  Activator.CreateInstance(typeof(T));
            return (T)o; 
        }
        else
        {
            try
            {
                Type type = typeof(T);
                if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
                {
                    type = Nullable.GetUnderlyingType(type);
                }
                return (T)Convert.ChangeType(elm.Value, type, theProvider); 
            }
            catch (Exception)
            {
                object o = Activator.CreateInstance(typeof(T));
                return (T)o; 
            }
        }
    }

आप इसे इस तरह से उपयोग कर सकते हैं:

iRes = helper.GetValueOrNull<int?>("top_overrun_length");
Assert.AreEqual(100, iRes);



decimal? dRes = helper.GetValueOrNull<decimal?>("top_overrun_bend_degrees");
Assert.AreEqual(new Decimal(10.1), dRes);

String strRes = helper.GetValueOrNull<String>("top_overrun_bend_degrees");
Assert.AreEqual("10.1", strRes);

2

यह एक मृत धागा हो सकता है, लेकिन मैं निम्नलिखित का उपयोग करता हूं:

public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct 
{
    return reader[columnName] as T?;
}

1
"प्रकार 'T' सामान्य प्रकार या विधि 'Nullable <T>' में पैरामीटर 'T' के रूप में उपयोग करने के लिए एक गैर-अशक्त मान प्रकार होना चाहिए
Ian Warburton

1

मैं सिर्फ अपने आप को एक ही समस्या का सामना करना पड़ा।

... = reader["myYear"] as int?; काम करता है और साफ है।

यह बिना किसी समस्या के किसी भी प्रकार के साथ काम करता है। यदि परिणाम DBNull है, तो रूपांतरण के विफल होने पर यह अशक्त हो जाता है।


वास्तव में, आप शायद int v=reader["myYear"]??-1;या इसके बजाय कुछ अन्य डिफ़ॉल्ट कर सकते हैं -1। हालाँकि, यह उन मुद्दों को सामने ला सकता है यदि मूल्य है DBNull...
nuri

1

मुझे पता है कि यह पुराना है, लेकिन यहां एक और समाधान है:

public static bool GetValueOrDefault<T>(this SqlDataReader Reader, string ColumnName, out T Result)
{
    try
    {
        object ColumnValue = Reader[ColumnName];

        Result = (ColumnValue!=null && ColumnValue != DBNull.Value) ? (T)ColumnValue : default(T);

        return ColumnValue!=null && ColumnValue != DBNull.Value;
    }
    catch
    {
        // Possibly an invalid cast?
        return false;
    }
}

अब, आप परवाह नहीं है अगर Tमूल्य या संदर्भ प्रकार था। केवल यदि फ़ंक्शन सही है, तो आपके पास डेटाबेस से एक उचित मूल्य है। उपयोग:

...
decimal Quantity;
if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity))
{
    // Do something with Quantity
}

यह दृष्टिकोण बहुत समान है int.TryParse("123", out MyInt);


यह अच्छा होगा यदि आप अपने नामकरण सम्मेलनों पर काम करते हैं। उनमें निरंतरता का अभाव है। एक जगह पर एक पूंजी के बिना एक चर है तो एक के साथ है। विधियों के मापदंडों के साथ भी ऐसा ही है।
मैरिनो Marimić

1
किया और किया! अब उम्मीद है कि कोड बेहतर होगा। बॉब की आपकी चाची :) सभी स्कूकुम है
नर्ची

0

एकाधिक जेनेरिक बाधाओं को एक OR फ़ैशन (कम प्रतिबंधात्मक) में नहीं जोड़ा जा सकता, केवल एक AND फ़ैशन (अधिक प्रतिबंधात्मक) में। मतलब कि एक विधि दोनों परिदृश्यों को संभाल नहीं सकती है। सामान्य बाधाओं का उपयोग विधि के लिए एक अद्वितीय हस्ताक्षर बनाने के लिए भी नहीं किया जा सकता है, इसलिए आपको 2 अलग-अलग विधि नामों का उपयोग करना होगा।

हालाँकि, आप यह सुनिश्चित करने के लिए सामान्य बाधाओं का उपयोग कर सकते हैं कि विधियाँ सही तरीके से उपयोग की जाती हैं।

मेरे मामले में, मैं विशेष रूप से अशक्त होना चाहता था, और किसी भी संभावित मूल्य प्रकार का डिफ़ॉल्ट मान कभी नहीं। GetValueOrDefault = बुरा। GetValueOrNull = अच्छा।

मैंने संदर्भ प्रकारों और मूल्य प्रकारों के बीच अंतर करने के लिए "नल" और "अशक्त" शब्दों का उपयोग किया। और यहाँ एक जोड़ी विस्तार विधियों का एक उदाहरण है जो मैंने लिखा था कि System.Linq.Enumerable वर्ग में FirstOrDefault पद्धति की प्रशंसा करता है।

    public static TSource FirstOrNull<TSource>(this IEnumerable<TSource> source)
        where TSource: class
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a class is null
        return result;
    }

    public static TSource? FirstOrNullable<TSource>(this IEnumerable<TSource?> source)
        where TSource : struct
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a nullable is null
        return result;
    }

0

छोटा तरीका:

public static T ValueOrDefault<T>(this DataRow reader, string columnName) => 
        reader.IsNull(columnName) ? default : (T) reader[columnName];

लौटने 0के लिए int, और nullके लिएint?

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