2010-03-22 137 views
131

對於任何給定的類型,我想知道它的默認值。運行時類型的默認值

在C#中,有一個名爲默認這樣做的像

object obj = default(Decimal); 

關鍵字,但我有一個類型(稱爲的myType)的實例,如果我說這個,

object obj = default(myType); 

它不不工作

有沒有這樣做的好方法? 我知道一個巨大的開關塊可以工作,但那不是一個好的選擇。

+0

你能解釋它爲什麼不起作用嗎?有錯誤嗎?它只是沒有回報你所期望的? – Gabe 2010-03-22 06:34:41

+0

@Josh,謝謝!你喜歡它。 – viky 2010-03-22 06:48:59

+1

@gabe,它適用於類型名稱,但不適用於該類型名稱的Type實例,我的意思是默認(Decimal)有效,但默認(typeof(Decimal))不會 – viky 2010-03-22 07:19:40

回答

214

這確實只有兩種可能:null引用類型和new myType()值類型(對應於0代表int,float等)所以你真的只需要考慮兩種情況:

object GetDefaultValue(Type t) 
{ 
    if (t.IsValueType) 
     return Activator.CreateInstance(t); 

    return null; 
} 

(因爲值類型總是有一個默認構造函數,所以對Activator.CreateInstance的調用永遠不會失敗)。

+12

我發佈了相同的解決方案,但我不確定如何處理可用的空白。事實證明,可變空值落入第一個分支,但Activator.CreateInstance(typeof(int?))實際返回null,因此全部結束。 – Josh 2010-03-22 06:08:27

+0

@Josh:有趣......我對我的解決方案做了一個快速測試,'int?'出來了預期的'null',但我沒有真正測試typeof(int?)。IsValueType'是否返回true,或者假。 – 2010-03-22 06:10:52

+0

是的。這使您在運行時重新實現C#編譯器的default()運算符。這很容易做到,但是,如果要延長違約規則以考慮新的場景,則必須更新代碼。 – 2010-03-22 06:11:08

9

如何像...

class Program 
{ 
    static void Main(string[] args) 
    { 
    PrintDefault(typeof(object)); 
    PrintDefault(typeof(string)); 
    PrintDefault(typeof(int)); 
    PrintDefault(typeof(int?)); 
    } 

    private static void PrintDefault(Type type) 
    { 
    Console.WriteLine("default({0}) = {1}", type, 
     DefaultGenerator.GetDefaultValue(type)); 
    } 
} 

public class DefaultGenerator 
{ 
    public static object GetDefaultValue(Type parameter) 
    { 
    var defaultGeneratorType = 
     typeof(DefaultGenerator<>).MakeGenericType(parameter); 

    return defaultGeneratorType.InvokeMember(
     "GetDefault", 
     BindingFlags.Static | 
     BindingFlags.Public | 
     BindingFlags.InvokeMethod, 
     null, null, new object[0]); 
    } 
} 

public class DefaultGenerator<T> 
{ 
    public static T GetDefault() 
    { 
    return default(T); 
    } 
} 

它產生以下輸出:

default(System.Object) = 
default(System.String) = 
default(System.Int32) = 0 
default(System.Nullable`1[System.Int32]) = 
+0

非常複雜。請參閱codeka的解決方案,以獲得更簡潔的方法。 – Josh 2010-03-22 06:10:14

+0

+1這就是一個很好的例子,但實際上我並不需要那麼多。 – viky 2010-03-22 06:47:50

+0

我想這取決於你的複雜定義。如果總共有兩個類和三個指令的二十四行代碼是「複雜的」,那麼我想你是對的...... Codeka的例子也有三條指令,所以我只能假設它是「額外的」上課,然後呢? – 2010-03-22 07:18:42

2

你是什麼意思的「默認值」?所有引用類型(「類」)都具有空值作爲默認值,而所有值類型都將具有根據this table的默認值。

+5

您提到的規則有一個明顯的例外。也就是說,可爲空的ValueType始終具有默認值null,而不是其基礎ValueType的默認值。可爲空的ValueType仍然是ValueType。還要記住,雖然泛型類定義(type.ContainsGenericParameters == true)在技術上被認爲是一個引用類型,但它沒有默認值,因爲它不能直接實例化。有關更多詳細信息和示例,請參閱http://stackoverflow.com/questions/2490244/default-value-of-a-type/7881481#7881481上的我的解決方案。 – 2011-10-24 21:01:36

+1

...更類似Nullable 類型是一個通用的ValueType類型,其默認值是可爲空的的一個實例,其Value屬性設置爲類型T的默認值,並將其HasValue屬性設置爲false。我的觀點是,一個可爲空的類型_is_從不爲空,這只是編譯器的魔力,使得在編寫代碼時可以更容易使用空值。你寫的是什麼:'object a = myNullable;'編譯器看到的是:'object a = myNullable.HasValue? myNullable.Value:null;'。因此技術上Nullables的缺省值_does_具有其「基礎」(通用)類型的默認值 - 但它並未公開。 – AnorZaken 2016-01-08 14:43:58

1

這裏是一個將返回默認值可空類型的函數(換句話說,這兩個DecimalDecimal?返回0):

public static object DefaultValue(Type maybeNullable) 
{ 
    Type underlying = Nullable.GetUnderlyingType(maybeNullable); 
    if (underlying != null) 
     return Activator.CreateInstance(underlying); 
    return Activator.CreateInstance(maybeNullable); 
} 
+2

您不希望爲可爲null的Type返回ValueType默認值,因爲這不是正確的默認值。可空類型的正確缺省值爲null。所以對於你的例子,Decimal應該有一個默認的0,但是十進制?應該有一個默認值爲null。請參閱http://stackoverflow.com/questions/2490244/default-value-of-a-type/7881481#7881481中的我的解決方案,它也適用於所有可爲空的類型。 – 2011-10-24 20:45:19

24

你也可以將其添加爲擴展方法體系.TYPE:

public static class TypeExtensions 
{ 
    public static object GetDefaultValue(this Type t) 
    { 
     if (t.IsValueType && Nullable.GetUnderlyingType(t) == null) 
      return Activator.CreateInstance(t); 
     else 
      return null; 
    } 
} 
+6

這'別的'讓我瘋狂:) – sam 2017-01-02 14:27:29

+0

返回(t.IsValueType && Nullable.GetUnderlyingType(t)== null)? Activator.CreateInstance(t):null;如果你不喜歡它猶豫不決! – coalvilledave 2017-07-11 15:42:12

+0

這不是關於只有一個'返回',這會使代碼在這種情況下變得混亂。真的只是刪除單詞'else',因爲如果你返回.. if(....)return Activator.CreateInstance(t);返回null; – sam 2017-07-12 07:29:39

15

在自己的系統中已經解決了這個問題,這裏是在運行時,已對數以千計類型測試的正確判斷任意類型的默認值的方法:

/// <summary> 
    /// [ <c>public static object GetDefault(this Type type)</c> ] 
    /// <para></para> 
    /// Retrieves the default value for a given Type 
    /// </summary> 
    /// <param name="type">The Type for which to get the default value</param> 
    /// <returns>The default value for <paramref name="type"/></returns> 
    /// <remarks> 
    /// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null. If a value type 
    /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an 
    /// exception. 
    /// </remarks> 
    /// <example> 
    /// To use this method in its native, non-extension form, make a call like: 
    /// <code> 
    ///  object Default = DefaultValue.GetDefault(someType); 
    /// </code> 
    /// To use this method in its Type-extension form, make a call like: 
    /// <code> 
    ///  object Default = someType.GetDefault(); 
    /// </code> 
    /// </example> 
    /// <seealso cref="GetDefault&lt;T&gt;"/> 
    public static object GetDefault(this Type type) 
    { 
     // If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null 
     if (type == null || !type.IsValueType || type == typeof(void)) 
      return null; 

     // If the supplied Type has generic parameters, its default value cannot be determined 
     if (type.ContainsGenericParameters) 
      throw new ArgumentException(
       "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type + 
       "> contains generic parameters, so the default value cannot be retrieved"); 

     // If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct/enum), return a 
     // default instance of the value type 
     if (type.IsPrimitive || !type.IsNotPublic) 
     { 
      try 
      { 
       return Activator.CreateInstance(type); 
      } 
      catch (Exception e) 
      { 
       throw new ArgumentException(
        "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe Activator.CreateInstance method could not " + 
        "create a default instance of the supplied value type <" + type + 
        "> (Inner Exception message: \"" + e.Message + "\")", e); 
      } 
     } 

     // Fail with exception 
     throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type + 
      "> is not a publicly-visible type, so the default value cannot be retrieved"); 
    } 

在這些示例中,GetDefault方法在靜態類DefaultValue中實現。調用此方法與像聲明:

 object Default = DefaultValue.GetDefault(someType); 

要使用GetDefault方法作爲類型的擴展方法,這樣稱呼它:

 object Default = someType.GetDefault(); 

這第二個,類型擴展方法是一個簡單的客戶端代碼語法,因爲它不需要在調用中引用包含DefaultValue類限定符。

GetDefault的上述運行時形式的工作原理與C#「default」關鍵字的相同語義相同,併產生相同的結果。

要使用GetDefault的通用形式,您可以訪問以下功能:

/// <summary> 
    /// [ <c>public static T GetDefault&lt; T &gt;()</c> ] 
    /// <para></para> 
    /// Retrieves the default value for a given Type 
    /// </summary> 
    /// <typeparam name="T">The Type for which to get the default value</typeparam> 
    /// <returns>The default value for Type T</returns> 
    /// <remarks> 
    /// If a reference Type or a System.Void Type is supplied, this method always returns null. If a value type 
    /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an 
    /// exception. 
    /// </remarks> 
    /// <seealso cref="GetDefault(Type)"/> 
    public static T GetDefault<T>() 
    { 
     return (T) GetDefault(typeof(T)); 
    } 

的一般形式的呼叫可以是這樣的:

 int? inDefaultVal = DefaultValue.GetDefault<int?>(); 

當然,上述通用GetDefault的形式對於C#來說是不必要的,因爲它和默認的(T)一樣。它僅適用於不支持「默認」關鍵字但支持泛型類型的.NET語言。在大多數情況下,通用形式是不必要的。

有用的推論方法是確定某個對象是否包含其Type類型的默認值的方法。我還依賴以下IsObjectSetToDefault方法用於該目的:

/// <summary> 
    /// [ <c>public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue)</c> ] 
    /// <para></para> 
    /// Reports whether a value of type T (or a null reference of type T) contains the default value for that Type 
    /// </summary> 
    /// <remarks> 
    /// Reports whether the object is empty or unitialized for a reference type or nullable value type (i.e. is null) or 
    /// whether the object contains a default value for a non-nullable value type (i.e. int = 0, bool = false, etc.) 
    /// <para></para> 
    /// NOTE: For non-nullable value types, this method introduces a boxing/unboxing performance penalty. 
    /// </remarks> 
    /// <param name="ObjectType">Type of the object to test</param> 
    /// <param name="ObjectValue">The object value to test, or null for a reference Type or nullable value Type</param> 
    /// <returns> 
    /// true = The object contains the default value for its Type. 
    /// <para></para> 
    /// false = The object has been changed from its default value. 
    /// </returns> 
    public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue) 
    { 
     // If no ObjectType was supplied, attempt to determine from ObjectValue 
     if (ObjectType == null) 
     { 
      // If no ObjectValue was supplied, abort 
      if (ObjectValue == null) 
      { 
       MethodBase currmethod = MethodInfo.GetCurrentMethod(); 
       string ExceptionMsgPrefix = currmethod.DeclaringType + " {" + currmethod + "} Error:\n\n"; 
       throw new ArgumentNullException(ExceptionMsgPrefix + "Cannot determine the ObjectType from a null Value"); 
      } 

      // Determine ObjectType from ObjectValue 
      ObjectType = ObjectValue.GetType(); 
     } 

     // Get the default value of type ObjectType 
     object Default = ObjectType.GetDefault(); 

     // If a non-null ObjectValue was supplied, compare Value with its default value and return the result 
     if (ObjectValue != null) 
      return ObjectValue.Equals(Default); 

     // Since a null ObjectValue was supplied, report whether its default value is null 
     return Default == null; 
    } 

上面IsObjectSetToDefault方法既可以在其天然形式被稱爲或作爲型類擴展訪問。