default(T) with empty collection instead of null

前端 未结 2 552
盖世英雄少女心
盖世英雄少女心 2020-12-14 11:27

I\'d like to have generic method which returns default value for passed type, but for collection type I\'d like to get empty collection instead of null, for example:

相关标签:
2条回答
  • 2020-12-14 11:57

    You can use the magic of a static constructor to do this efficiently. To use the default value in code, simply use Default<T>.Value. The value will only be evaluated for any given type T once for the duration of your application.

    public static class Default<T>
    {
        private static readonly T _value;
    
        static Default()
        {
            if (typeof(T).IsArray)
            {
                if (typeof(T).GetArrayRank() > 1)
                    _value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), new int[typeof(T).GetArrayRank()]);
                else
                    _value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), 0);
                return;
            }
    
            if (typeof(T) == typeof(string))
            {
                // string is IEnumerable<char>, but don't want to treat it like a collection
                _value = default(T);
                return;
            }
    
            if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
            {
                // check if an empty array is an instance of T
                if (typeof(T).IsAssignableFrom(typeof(object[])))
                {
                    _value = (T)(object)new object[0];
                    return;
                }
    
                if (typeof(T).IsGenericType && typeof(T).GetGenericArguments().Length == 1)
                {
                    Type elementType = typeof(T).GetGenericArguments()[0];
                    if (typeof(T).IsAssignableFrom(elementType.MakeArrayType()))
                    {
                        _value = (T)(object)Array.CreateInstance(elementType, 0);
                        return;
                    }
                }
    
                throw new NotImplementedException("No default value is implemented for type " + typeof(T).FullName);
            }
    
            _value = default(T);
        }
    
        public static T Value
        {
            get
            {
                return _value;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-14 11:58

    IList<object> is not a collection type, it's an interface. There are dozens of possible classes you could return.

    If you pass in an actual collection type, you can do this:

    public static T GetDefault<T>() where T : new
    {
       if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
       {
          return new T();
       }
       return default(T);   
    }
    
    GetDefault<List<object>>();
    

    To handle both empty collections, and null values of types without default constructors, you can do it like this:

    public static T GetDefault<T>()
    {
       if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
       {
                if (typeof(T).IsGenericType)
                {
                    Type T_template = typeof(T).GetGenericTypeDefinition();
                    if (T_template == typeof(IEnumerable<>))
                    {
                        return (T)Activator.CreateInstance(typeof(Enumerable).MakeGenericType(typeof(T).GetGenericArguments()));
                    }
                    if (T_template == typeof(IList<>))
                    {
                        return (T)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T).GetGenericArguments()));
                    }
                }
    
          try {
              return Activator.CreateInstance<T>();
          }
          catch (MissingMethodException) {} // no default exists for this type, fall-through to returning null
       }
       return default(T);   
    }
    
    0 讨论(0)
提交回复
热议问题