Программный эквивалент по умолчанию (Тип) - PullRequest
476 голосов
/ 28 ноября 2008

Я использую отражение, чтобы пройти через свойства Type и установить для определенных типов их значения по умолчанию. Теперь я мог бы переключить тип и установить default(Type) явно, но я бы предпочел сделать это в одной строке. Есть ли программный эквивалент значения по умолчанию?

Ответы [ 13 ]

643 голосов
/ 09 декабря 2008
  • В случае типа значения используйте Activator.CreateInstance , и оно должно работать нормально.
  • При использовании ссылочного типа просто возвращать ноль
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

В более новой версии .net, такой как стандарт .net, type.IsValueType необходимо записать как type.GetTypeInfo().IsValueType

96 голосов
/ 05 ноября 2011

Почему бы не вызвать метод, который возвращает default (T) с отражением? Вы можете использовать GetDefault любого типа с:

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

    public T GetDefaultGeneric<T>()
    {
        return default(T);
    }
80 голосов
/ 24 апреля 2012

Вы можете использовать PropertyInfo.SetValue(obj, null). Если вызывается для типа значения, он даст вам значение по умолчанию. Это поведение задокументировано в .NET 4.0 и в .NET 4.5 .

54 голосов
/ 04 октября 2012

Если вы используете .NET 4.0 или выше и вам нужна программная версия, которая не является кодификацией правил, определенных вне кода , вы можете создать Expression, скомпилируйте и запустите на лету.

Следующий метод расширения примет Type и получит значение, возвращаемое из default(T) через Default метод в Expression класс:

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 экземпляров и не используете его постоянно, память, используемая кешем может перевесить преимущества.

37 голосов
/ 27 октября 2010

Почему вы говорите, что дженерики за кадром?

    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);
    }
22 голосов
/ 14 ноября 2012

Это оптимизированное решение Флема:

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;
        }
    }
}
7 голосов
/ 16 октября 2010

Выбранный ответ - хороший ответ, но будьте осторожны с возвращаемым объектом.

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

Экстраполяция ...

string test = GetDefault(typeof(string));
if (test is string)
     Console.WriteLine("This will never be hit.");
5 голосов
/ 26 декабря 2011

Выражения могут помочь здесь:

    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();
    }

Я не тестировал этот фрагмент, но я думаю, что он должен выдавать "типизированные" нули для ссылочных типов.

3 голосов
/ 28 ноября 2008

Я делаю ту же задачу, как это.

//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;
    }
3 голосов
/ 28 ноября 2008

Пока что не могу найти ничего простого и элегантного, но у меня есть одна идея: если вы знаете тип свойства, которое вы хотите установить, вы можете написать свой default(T). Существует два случая: T - это тип значения, а T - это ссылочный тип. В этом можно убедиться, проверив T.IsValueType. Если T является ссылочным типом, то вы можете просто установить его на null. Если T является типом значения, то он будет иметь конструктор по умолчанию без параметров, который можно вызвать для получения «пустого» значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...