【问题标题】:Default value of a type at Runtime [duplicate]运行时类型的默认值 [重复]
【发布时间】:2011-01-30 05:17:32
【问题描述】:

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

在 C# 中,有一个叫做 default 的关键字可以这样做

object obj = default(Decimal);

但我有一个 Type 实例(称为 myType),如果我这样说,

object obj = default(myType);

没用

有什么好的方法吗? 我知道一个巨大的开关块会起作用,但这不是一个好的选择。

【问题讨论】:

  • 你能解释一下为什么它不起作用吗?有错误吗?它只是没有返回您期望的结果吗?
  • @gabe,它适用于类型名称,但不适用于该类型名称的类型实例,我的意思是默认(十进制)有效但默认(类型(十进制))不起作用

标签: c# types default


【解决方案1】:

诸如...之类的东西怎么样?

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]) =

【讨论】:

  • 相当复杂。请参阅 codeka 的解决方案以获得更简洁的方法。
  • 我想这取决于你对复杂的定义。如果总共有两个类和三个指令的二十四行代码是“复杂的”,那么我想你是对的...... Codeka 的示例也有三个指令,所以我只能假设它是“额外的”那么上课呢?
  • 除了不必要的复杂性之外,这个答案还引入了显着的性能损失,即它使用 MakeGenericType 和调用外部泛型方法。请考虑我在stackoverflow.com/questions/2490244/default-value-of-a-type/… 的解决方案来解决这些问题。
【解决方案2】:

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

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

    return null;
}

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

【讨论】:

  • 我发布了相同的解决方案,但我不确定如何处理可空值。事实证明,可空对象属于第一个分支,但 Activator.CreateInstance(typeof(int?)) 实际上返回 null 所以一切正常。
  • @gabe,没有 OP 说他想使用 default(myType) 除了当他在运行时只有一个 Type 实例时它需要一个常量类型或泛型类型参数。我读他的问题和codeka一样。
  • 供参考(并且因为我遇到了问题),System.Void 被认为是一个 ValueType,但会导致 CreateInstance 上的异常。如果您使用此代码获取方法的默认返回值,则需要考虑 System.Void !
  • @Shimmy 为什么字符串会失败? string 的默认值为 null。
  • @Gabor 实际上在这种情况下,Activator.CreateInstance 总是返回一个System.Object,所以如果你要求它创建一个ValueType 的实例,它必须将它装箱。 Nullable&lt;T&gt; 在运行时为装箱设置了特殊大小写,因此 Nullable 包装器消失了,只返回了 T。但是,当HasValue 为假时,object 引用只是null。所以,它真的 合法的一个实际的 null 引用来自例如Activator.CreateInstance(typeof(int?)),而不是 int?。内部可能有一个int?,但在拳击过程中消失了。
【解决方案3】:

“默认值”是什么意思?所有引用类型(“类”)都将 null 作为默认值,而所有值类型都将根据this table 拥有其默认值。

【讨论】:

  • 您提到的规则有一个明显的例外。也就是说,可为空的 ValueType 始终具有 null 的默认值,而不是其基础 ValueType 的默认值。尽管如此,一个可为空的 ValueType 仍然是一个 ValueType。还要记住,虽然泛型类定义(type.ContainsGenericParameters == true)在技术上被认为是引用类型,但它没有默认值,因为它不能直接实例化。请参阅我在stackoverflow.com/questions/2490244/default-value-of-a-type/… 的解决方案以获取更多详细信息和示例。
  • ...更像 Nullable 类型是一个通用的 ValueType,它的默认值是 Nullable 的一个实例,其 Value 属性设置为 T 类型的默认值及其 HasValue属性设置为假。我的观点是,可空类型 is 永远不会为空,这只是编译器的魔力,可以让可空对象在编写代码时更易于使用。你写的:object a = myNullable; 编译器看到的:object a = myNullable.HasValue ? myNullable.Value : null;。因此,从技术上讲,Nullables 的默认值 确实 具有其“基础”(通用)类型的默认值 - 但它没有公开。
【解决方案4】:

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

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

【讨论】:

  • 您不想为可空类型返回 ValueType 默认值,因为这不是正确的默认值。可空类型的正确默认值为空。所以关于你的例子,十进制应该有一个默认值0,但是十进制?应该有一个默认值 null。在stackoverflow.com/questions/2490244/default-value-of-a-type/… 上详细查看我的解决方案,它也适用于所有可为空的类型。
【解决方案5】:

您也可以将其作为扩展方法添加到 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;
    }
}

【讨论】:

  • 那个'else'让我发疯:)
  • return (t.IsValueType && Nullable.GetUnderlyingType(t) == null) ? Activator.CreateInstance(t) : null;如果你不喜欢它挂在那里!
  • 这并不是说只有一个“返回”,在这种情况下它会使代码变得混乱。真的只是删除单词'else',因为在 if you return.. if(....)return Activator.CreateInstance(t);返回空值;
  • 如果我想简洁,我会使用三元运算符 - 它看起来不乱。包含 else 意味着代码更容易快速掌握。删除它并只使用“return null;”引入了隐含性。
  • Nullable.GetUnderlyingType(t) == null 是干什么用的?什么情况下不能为空?
【解决方案6】:

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

    /// <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 方法用作 Type 的扩展方法,请像这样调用它:

        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) 相同。它仅对不支持“默认”关键字但支持泛型类型的 .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 方法既可以以其本机形式调用,也可以作为类型类扩展访问。

【讨论】:

    猜你喜欢
    • 2013-01-27
    • 2011-03-01
    • 2012-12-17
    • 2010-09-27
    • 1970-01-01
    • 2011-02-14
    • 2018-11-27
    • 2011-05-02
    相关资源
    最近更新 更多