डिफ़ॉल्ट के प्रोग्रामेटिक समकक्ष (प्रकार)


514

मैं प्रतिबिंब का उपयोग Typeगुणों के माध्यम से करने के लिए कर रहा हूँ और उनके डिफ़ॉल्ट के लिए कुछ प्रकार सेट करें। अब, मैं टाइप पर स्विच कर सकता हूं और default(Type)स्पष्ट रूप से सेट कर सकता हूं , लेकिन मैं इसे एक पंक्ति में करूंगा। क्या कोई प्रोग्रामेटिक डिफ़ॉल्ट के बराबर है?


यह काम करना चाहिए: Nullable <T> a = new Nullable <T> ()। GetValueOrDefault ();
dancer42

जवाबों:


694
  • मान प्रकार के मामले में Activator.CreateInstance का उपयोग करें और इसे ठीक काम करना चाहिए।
  • संदर्भ प्रकार का उपयोग करते समय बस अशक्त लौटें
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

.Net मानक के रूप में .net के नए संस्करण में, के type.IsValueTypeरूप में लिखा जाना चाहिएtype.GetTypeInfo().IsValueType


22
यह एक बॉक्सिंग मान प्रकार लौटाएगा और इसलिए डिफ़ॉल्ट (प्रकार) का सटीक समकक्ष नहीं है। हालांकि, यह उतना ही करीब है जितना आप जेनरिक के बिना जा रहे हैं।
रसेल गिडिंग ने

8
तो क्या? यदि आप एक प्रकार पाते हैं, default(T) != (T)(object)default(T) && !(default(T) != default(T))तो आपके पास एक तर्क है, अन्यथा यह कोई फर्क नहीं पड़ता कि यह बॉक्सिंग है या नहीं, क्योंकि वे बराबर हैं।
मिगेल एंजेलो

7
विधेय का आखिरी टुकड़ा ऑपरेटर को ओवरलोडिंग के साथ धोखा देने से बचना है ... एक default(T) != default(T)झूठे को वापस कर सकता है , और वह धोखा है! =)
मिगेल एंजेलो

4
इससे मुझे बहुत मदद मिली, लेकिन मुझे लगा कि मुझे एक बात जोड़नी चाहिए जो इस प्रश्न को खोजने वाले कुछ लोगों के लिए उपयोगी हो सकती है - अगर आप दिए गए प्रकार की एक सरणी चाहते हैं तो भी एक समान विधि है , और आप इसका उपयोग करके प्राप्त कर सकते हैं Array.CreateInstance(type, length)
डारेल हॉफमैन

4
क्या आप एक अज्ञात मूल्य प्रकार का उदाहरण बनाने के बारे में चिंता नहीं करते हैं? इससे संपार्श्विक प्रभाव पड़ सकता है।
यगोरमुर्ती

103

प्रतिबिंब के साथ डिफ़ॉल्ट (T) को वापस करने वाली विधि को क्यों नहीं कहते हैं? आप किसी भी प्रकार के GetDefault का उपयोग कर सकते हैं:

    public object GetDefault(Type t)
    {
        return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
    }

    public T GetDefaultGeneric<T>()
    {
        return default(T);
    }

7
यह शानदार है क्योंकि यह बहुत सरल है। हालांकि यह यहां सबसे अच्छा समाधान नहीं है, यह ध्यान में रखना एक महत्वपूर्ण समाधान है क्योंकि यह तकनीक समान परिस्थितियों में बहुत उपयोगी हो सकती है।
विन्यासकर्ता

यदि आप इसके बजाय (ओवरलोडिंग) जेनेरिक विधि को "गेटडफॉल्ट" कहते हैं, तो यह करें: यह। Gypeype ()। GetMethod ("GetDefault", नया प्रकार [0])। <AS_IS>
स्टीफन स्टीगर

2
ध्यान रखें, यह कार्यान्वयन स्वीकृत उत्तर की तुलना में बहुत धीमा (प्रतिबिंब के कारण) है। यह अभी भी व्यवहार्य है, लेकिन आपको प्रदर्शन को बेहतर बनाने के लिए GetMethod () / MakeGenericMethod () कॉल के लिए कुछ कैशिंग सेटअप करने की आवश्यकता होगी।
डग

1
यह संभव है कि प्रकार तर्क शून्य हो। ईजी MethodBase.ResultType () की एक शून्य विधि "Void" या FullName "System.Void" नाम के साथ एक प्रकार की वस्तु लौटाएगी। इसलिए मैंने एक गार्ड लगाया: अगर (t.FullName == "System.Void") वापसी शून्य; समाधान के लिए धन्यवाद।
वालो

8
बेहतर उपयोग nameof(GetDefaultGeneric)करता है, तो आप कर सकते हैं, के बजाय"GetDefaultGeneric"
Mugen

87

आप उपयोग कर सकते हैं PropertyInfo.SetValue(obj, null)। यदि मूल्य प्रकार पर कॉल किया जाता है तो यह आपको डिफ़ॉल्ट देगा। यह व्यवहार .NET 4.0 और .NET 4.5 में प्रलेखित है


7
इस विशिष्ट प्रश्न के लिए - एक प्रकार के गुणों को गर्त में डालना और उन्हें "डिफ़ॉल्ट" पर सेट करना - यह शानदार ढंग से काम करता है। जब मैं प्रतिबिंब का उपयोग करके किसी SqlDataReader से किसी ऑब्जेक्ट में परिवर्तित करता हूं तो इसका उपयोग करता हूं।
अर्नो पीटर्स

57

यदि आप .NET 4.0 या इसके बाद के संस्करण का उपयोग कर रहे हैं और आप एक प्रोग्रामेटिक संस्करण चाहते हैं जो कोड के बाहर परिभाषित नियमों का एक कोडीकरण नहीं है , तो आप इसे बना सकते हैंExpression -संकलन और इसे ऑन-द-फ्लाई चला सकते हैं।

निम्नलिखित विस्तार विधि एक लेगी Typeऔर वर्ग पर विधि केdefault(T) माध्यम से लौटाए गए मान प्राप्त करेगी :DefaultExpression

public static T GetDefaultValue<T>()
{
    // We want an Func<T> which returns the default.
    // Create that expression here.
    Expression<Func<T>> e = Expression.Lambda<Func<T>>(
        // The default value, always get what the *code* tells us.
        Expression.Default(typeof(T))
    );

    // Compile and return the value.
    return e.Compile()();
}

public static object GetDefaultValue(this Type type)
{
    // Validate parameters.
    if (type == null) throw new ArgumentNullException("type");

    // We want an Func<object> which returns the default.
    // Create that expression here.
    Expression<Func<object>> e = Expression.Lambda<Func<object>>(
        // Have to convert to object.
        Expression.Convert(
            // The default value, always get what the *code* tells us.
            Expression.Default(type), typeof(object)
        )
    );

    // Compile and return the value.
    return e.Compile()();
}

आपको इसके आधार पर उपरोक्त मूल्य को भी कैश करना चाहिए Type, लेकिन जागरूक रहें यदि आप इसे बड़ी संख्या में Typeउदाहरणों के लिए कॉल कर रहे हैं , और इसका लगातार उपयोग न करें, तो कैश द्वारा खपत की गई मेमोरी लाभों से आगे निकल सकती है।


4
प्रदर्शन के लिए 'return type.IsValueType? Activator.CreateInstance (प्रकार): अशक्त;) e.Compile () से 1000x अधिक तेज़ है;
सायरस

1
@ साइरस मुझे पूरा यकीन है कि अगर आप कैश करेंगे तो यह दूसरा रास्ता होगा e.Compile()। वह भावों की पूरी बात है।
नवफाल

2
एक बेंचमार्क दौड़ा। जाहिर है, परिणाम को e.Compile()कैश किया जाना चाहिए, लेकिन यह मानते हुए, यह विधि लगभग 14x है जैसे कि उपवास के लिए long। बेंचमार्क और परिणाम के लिए gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b देखें ।
पीटर वैन जिंकेल

3
ब्याज से बाहर, e.Compile()बजाय कैश क्यों e.Compile()()? यानी रनटाइम पर एक प्रकार का डिफ़ॉल्ट प्रकार बदल सकता है? यदि नहीं (जैसा कि मेरा मानना ​​है कि मामला है) आप संकलित अभिव्यक्ति के बजाय परिणाम को कैश कर सकते हैं, जिससे प्रदर्शन में और सुधार होना चाहिए।
जॉनब्लेवन

3
@JohnLBevan - हाँ, और फिर इससे कोई फर्क नहीं पड़ेगा कि परिणाम प्राप्त करने के लिए आप किस तकनीक का उपयोग करते हैं - सभी में बहुत तेज़ परिशोधन प्रदर्शन (एक शब्दकोश खोज) होगा।
डैनियल ईयरविकेर

38

आप क्यों कहते हैं कि जेनेरिक तस्वीर से बाहर हैं?

    public static object GetDefault(Type t)
    {
        Func<object> f = GetDefault<object>;
        return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null);
    }

    private static T GetDefault<T>()
    {
        return default(T);
    }

प्रतीक विधि को हल नहीं कर सकते। विंडोज के लिए एक पीसीएल का उपयोग करना।
चतुर

1
चलाने के समय में सामान्य विधि बनाना कितना महंगा है, और फिर इसे कई हजार बार उपयोग करें?
सी। टवाल्ट

1
मैं कुछ इस तरह से सोच रहा था। मेरे लिए सबसे अच्छा और सबसे सुंदर समाधान। कॉम्पैक्ट फ्रेमवर्क 2.0 पर भी काम करता है। यदि आप प्रदर्शन के बारे में चिंतित हैं, तो आप हमेशा जेनेरिक विधि को कैश कर सकते हैं, क्या आप नहीं कर सकते?
बार्ट

यह समाधान बिल्कुल सूट करता है! धन्यवाद!
लछेज़र लालोव

25

यह फ्लेम का समाधान अनुकूलित है:

using System.Collections.Concurrent;

namespace System
{
    public static class TypeExtension
    {
        //a thread-safe way to hold default instances created at run-time
        private static ConcurrentDictionary<Type, object> typeDefaults =
           new ConcurrentDictionary<Type, object>();

        public static object GetDefaultValue(this Type type)
        {
            return type.IsValueType
               ? typeDefaults.GetOrAdd(type, Activator.CreateInstance)
               : null;
        }
    }
}

2
वापसी का एक संक्षिप्त संस्करण:return type.IsValueType ? typeDefaults.GetOrAdd(type, Activator.CreateInstance) : null;
मार्क व्हिटफेल्ड

3
उत्परिवर्ती संरचनाओं के बारे में क्या? क्या आप जानते हैं कि यह (और कानूनी) एक बॉक्सिंग संरचना के क्षेत्रों को संशोधित करना संभव है, ताकि डेटा बदल जाए?
इलिडान्स 4 मोनिका को

@ IllidanS4 क्योंकि विधि का नाम इसका तात्पर्य केवल डिफ़ॉल्ट वैल्यू टाइप के मानों के लिए है।
आराध्य

8

चुना गया उत्तर एक अच्छा उत्तर है, लेकिन वापस लौटी वस्तु से सावधान रहें।

string test = null;
string test2 = "";
if (test is string)
     Console.WriteLine("This will never be hit.");
if (test2 is string)
     Console.WriteLine("Always hit.");

Extrapolating ...

string test = GetDefault(typeof(string));
if (test is string)
     Console.WriteLine("This will never be hit.");

14
सच है, लेकिन यह डिफ़ॉल्ट (स्ट्रिंग) के साथ-साथ हर दूसरे संदर्भ प्रकार के लिए भी है ...
TDaver

स्ट्रिंग एक विषम पक्षी है - एक मूल्य प्रकार होने के नाते जो अशक्त भी वापस आ सकता है। यदि आप चाहते हैं कि कोड string.empty वापस लौटे तो इसके लिए एक विशेष मामला जोड़ें
Dror Helper

15
@ डोर - स्ट्रिंग एक अपरिवर्तनीय संदर्भ प्रकार है, न कि मूल्य प्रकार।
ljs

@ क्रोनोज़ आप सही हैं - मेरा मतलब था कि स्ट्रिंग को स्ट्रिंग के द्वारा नियंत्रित किया जा सकता है। जरूरत के अनुसार खाली या रिक्त।
Dror Helper

5

अभिव्यक्तियाँ यहाँ मदद कर सकती हैं:

    private static Dictionary<Type, Delegate> lambdasMap = new Dictionary<Type, Delegate>();

    private object GetTypedNull(Type type)
    {
        Delegate func;
        if (!lambdasMap.TryGetValue(type, out func))
        {
            var body = Expression.Default(type);
            var lambda = Expression.Lambda(body);
            func = lambda.Compile();
            lambdasMap[type] = func;
        }
        return func.DynamicInvoke();
    }

मैंने इस स्निपेट का परीक्षण नहीं किया, लेकिन मुझे लगता है कि इसे संदर्भ प्रकारों के लिए "टाइप" नल का उत्पादन करना चाहिए।


1
"typed" nulls- समझाइए। आप किस वस्तु पर लौट रहे हैं? यदि आप किसी प्रकार की वस्तु लौटाते हैं type, लेकिन उसका मूल्य है null, तो यह नहीं है - नहीं हो सकता है - इसके अलावा कोई अन्य जानकारी नहीं है null। आप एक nullमान क्वेरी नहीं कर सकते हैं , और यह पता लगा सकते हैं कि यह किस प्रकार का माना जाता है। यदि आप अशक्त नहीं हैं, लेकिन वापस .. मैं नहीं जानता कि क्या .., तो यह काम नहीं करेगा null
टूलमेकर

3

अभी तक कुछ भी सरल और सुरुचिपूर्ण नहीं पाया जा सकता है, लेकिन मेरे पास एक विचार है: यदि आप जानते हैं कि जिस प्रकार की संपत्ति आप सेट करना चाहते हैं, तो आप अपना खुद का लिख ​​सकते हैं default(T) । दो मामले हैं - Tएक मूल्य प्रकार है, और Tएक संदर्भ प्रकार है। इसे आप चेक करके देख सकते हैं T.IsValueType। यदि Tएक संदर्भ प्रकार है, तो आप बस इसे सेट कर सकते हैं null। यदि Tएक मूल्य प्रकार है, तो इसमें एक डिफ़ॉल्ट पैरामीटर रहित निर्माता होगा जिसे आप "रिक्त" मान प्राप्त करने के लिए कॉल कर सकते हैं।


3

मैं इसी तरह का कार्य करता हूं।

//in MessageHeader 
   private void SetValuesDefault()
   {
        MessageHeader header = this;             
        Framework.ObjectPropertyHelper.SetPropertiesToDefault<MessageHeader>(this);
   }

//in ObjectPropertyHelper
   public static void SetPropertiesToDefault<T>(T obj) 
   {
            Type objectType = typeof(T);

            System.Reflection.PropertyInfo [] props = objectType.GetProperties();

            foreach (System.Reflection.PropertyInfo property in props)
            {
                if (property.CanWrite)
                {
                    string propertyName = property.Name;
                    Type propertyType = property.PropertyType;

                    object value = TypeHelper.DefaultForType(propertyType);
                    property.SetValue(obj, value, null);
                }
            }
    }

//in TypeHelper
    public static object DefaultForType(Type targetType)
    {
        return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
    }

2

Dror के उत्तर के बराबर लेकिन एक विस्तार विधि के रूप में:

namespace System
{
    public static class TypeExtensions
    {
        public static object Default(this Type type)
        {
            object output = null;

            if (type.IsValueType)
            {
                output = Activator.CreateInstance(type);
            }

            return output;
        }
    }
}

2

@Rob Fonseca-Ensor के समाधान के लिए थोड़ा समायोजन : निम्न एक्सटेंशन विधि भी .Net मानक पर काम करती है क्योंकि मैं GetMuntod के बजाय GetRuntimeMethod का उपयोग करता हूं।

public static class TypeExtensions
{
    public static object GetDefault(this Type t)
    {
        var defaultValue = typeof(TypeExtensions)
            .GetRuntimeMethod(nameof(GetDefaultGeneric), new Type[] { })
            .MakeGenericMethod(t).Invoke(null, null);
        return defaultValue;
    }

    public static T GetDefaultGeneric<T>()
    {
        return default(T);
    }
}

... और गुणवत्ता के बारे में देखभाल करने वालों के लिए इकाई परीक्षण:

[Fact]
public void GetDefaultTest()
{
    // Arrange
    var type = typeof(DateTime);

    // Act
    var defaultValue = type.GetDefault();

    // Assert
    defaultValue.Should().Be(default(DateTime));
}

0
 /// <summary>
    /// returns the default value of a specified type
    /// </summary>
    /// <param name="type"></param>
    public static object GetDefault(this Type type)
    {
        return type.IsValueType ? (!type.IsGenericType ? Activator.CreateInstance(type) : type.GenericTypeArguments[0].GetDefault() ) : null;
    }

2
Nullable<T>प्रकारों के लिए काम नहीं करता है : यह उसके बराबर नहीं लौटता है default(Nullable<T>)जो होना चाहिए null। Dror द्वारा स्वीकृत उत्तर बेहतर काम करता है।
कूर

जांच कर सकते हैं कि क्या
अशक्त

0

यह काम करना चाहिए: Nullable<T> a = new Nullable<T>().GetValueOrDefault();

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