代码之家  ›  专栏  ›  技术社区  ›  viky

运行时类型的默认值[重复]

  •  142
  • viky  · 技术社区  · 15 年前

    对于任何给定的类型,我想知道它的默认值。

    在C中,有一个名为default的关键字,用于执行如下操作

    object obj = default(Decimal);
    

    但是我有一个类型的实例(称为mytype),如果我这样说,

    object obj = default(myType);
    

    它不起作用

    有什么好办法吗? 我知道一个巨大的开关组可以工作,但这不是一个好的选择。

    6 回复  |  直到 8 年前
        1
  •  232
  •   Andrew Bullock    11 年前

    实际上只有两种可能性: null 用于参考类型和 new myType() 对于值类型(对应于int、float等的0),因此您只需要考虑两种情况:

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

    (因为值类型总是有一个默认的构造函数,所以对activator.createInstance的调用永远不会失败)。

        2
  •  29
  •   DavidWainwright    14 年前

    也可以将其作为扩展方法添加到System.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;
        }
    }
    
        3
  •  18
  •   BartoszKP    8 年前

    在我自己的系统中解决了这个问题,下面是一个在运行时正确确定任意类型的默认值的方法,该方法已经针对数千种类型进行了测试:

        /// <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来说是不必要的,因为它的工作方式与default(t)相同。它只对不支持“default”关键字但支持泛型类型的.NET语言有用。在大多数情况下,一般形式是不必要的。

    一个有用的推论方法是确定一个对象是否包含其类型的默认值。为此,我还依赖以下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 方法可以以其本机形式调用,也可以作为类型类扩展访问。

        4
  •  10
  •   MaxGuernseyIII    15 年前

    比如……

    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]) =
    
        5
  •  2
  •   Michael Stum    15 年前

    “默认值”是什么意思?所有引用类型(“类”)的默认值都为空,而所有值类型的默认值都将根据 this table .

        6
  •  1
  •   Gabe Timothy Khouri    15 年前

    这是一个函数,它将返回可为空类型的默认值(换句话说,它为两种类型都返回0 Decimal Decimal? ):

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